diff --git a/doc/src/sgml/func/func-admin.sgml b/doc/src/sgml/func/func-admin.sgml index e7ea16f73b3..3800cf3da09 100644 --- a/doc/src/sgml/func/func-admin.sgml +++ b/doc/src/sgml/func/func-admin.sgml @@ -2165,6 +2165,85 @@ SELECT pg_restore_attribute_stats( + + + + pg_restore_extended_stats + + pg_restore_extended_stats ( + VARIADIC kwargs "any" ) + boolean + + + Creates or updates statistics for statistics objects. Ordinarily, + these statistics are collected automatically or updated as a part of + or , so + it's not necessary to call this function. However, it is useful + after a restore to enable the optimizer to choose better plans if + ANALYZE has not been run yet. + + + The tracked statistics may change from version to version, so + arguments are passed as pairs of argname + and argvalue in the form: + + SELECT pg_restore_extended_stats( + 'arg1name', 'arg1value'::arg1type, + 'arg2name', 'arg2value'::arg2type, + 'arg3name', 'arg3value'::arg3type); + + + + For example, to set the n_distinct, + dependencies, and exprs + values for the statistics object myschema.mystatsobj: + + SELECT pg_restore_extended_stats( + 'schemaname', 'tab_schema'::name, + 'relname', 'tab_name'::name, + 'statistics_schemaname', 'stats_schema'::name, + 'statistics_name', 'stats_name'::name, + 'inherited', false, + 'n_distinct', '[{"attributes" : [2,3], "ndistinct" : 4}]'::pg_ndistinct); + + + + The required arguments are schemaname with a value + of type name, for the schema of the table to which the + statistics are related to, relname with a value + of type name, for the table to which the statistics are + related to, statistics_schemaname + with a value of type name, which specifies the statistics + object's schema, statistics_name with a value of + type name, which specifies the name of the statistics + object and inherited, which specifies whether + the statistics include values from child tables. + + + Other arguments are the names and values of statistics corresponding + to columns in pg_stats_ext + . + This function currently supports n_distinct. + + + Additionally, this function accepts argument name + version of type integer, which + specifies the server version from which the statistics originated. + This is anticipated to be helpful in porting statistics from older + versions of PostgreSQL. + + + Minor errors are reported as a WARNING and + ignored, and remaining statistics will still be restored. If all + specified statistics are successfully restored, returns + true, otherwise false. + + + The caller must have the MAINTAIN privilege on the + table or be the owner of the database. + + + diff --git a/src/backend/statistics/extended_stats_funcs.c b/src/backend/statistics/extended_stats_funcs.c index b4b1bf26463..269fdabdfc0 100644 --- a/src/backend/statistics/extended_stats_funcs.c +++ b/src/backend/statistics/extended_stats_funcs.c @@ -24,8 +24,10 @@ #include "catalog/pg_statistic_ext_data.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "statistics/extended_stats_internal.h" #include "statistics/stat_utils.h" #include "utils/acl.h" +#include "utils/array.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" @@ -42,6 +44,7 @@ enum extended_stats_argnum STATSCHEMA_ARG, STATNAME_ARG, INHERITED_ARG, + NDISTINCT_ARG, NUM_EXTENDED_STATS_ARGS, }; @@ -56,13 +59,33 @@ static struct StatsArgInfo extarginfo[] = [STATSCHEMA_ARG] = {"statistics_schemaname", TEXTOID}, [STATNAME_ARG] = {"statistics_name", TEXTOID}, [INHERITED_ARG] = {"inherited", BOOLOID}, + [NDISTINCT_ARG] = {"n_distinct", PG_NDISTINCTOID}, [NUM_EXTENDED_STATS_ARGS] = {0}, }; +static bool extended_statistics_update(FunctionCallInfo fcinfo); + static HeapTuple get_pg_statistic_ext(Relation pg_stext, Oid nspoid, const char *stxname); static bool delete_pg_statistic_ext_data(Oid stxoid, bool inherited); +/* + * Track the extended statistics kinds expected for a pg_statistic_ext + * tuple. + */ +typedef struct +{ + bool ndistinct; + bool dependencies; + bool mcv; + bool expressions; +} StakindFlags; + +static void expand_stxkind(HeapTuple tup, StakindFlags *enabled); +static void upsert_pg_statistic_ext_data(const Datum *values, + const bool *nulls, + const bool *replaces); + /* * Fetch a pg_statistic_ext row by name and namespace OID. */ @@ -109,6 +132,278 @@ get_pg_statistic_ext(Relation pg_stext, Oid nspoid, const char *stxname) return SearchSysCacheCopy1(STATEXTOID, ObjectIdGetDatum(stxoid)); } +/* + * Decode the stxkind column so that we know which stats types to expect, + * returning a StakindFlags set depending on the stats kinds expected by + * a pg_statistic_ext tuple. + */ +static void +expand_stxkind(HeapTuple tup, StakindFlags *enabled) +{ + Datum datum; + ArrayType *arr; + char *kinds; + + datum = SysCacheGetAttrNotNull(STATEXTOID, + tup, + Anum_pg_statistic_ext_stxkind); + arr = DatumGetArrayTypeP(datum); + if (ARR_NDIM(arr) != 1 || ARR_HASNULL(arr) || ARR_ELEMTYPE(arr) != CHAROID) + elog(ERROR, "stxkind is not a one-dimension char array"); + + kinds = (char *) ARR_DATA_PTR(arr); + + for (int i = 0; i < ARR_DIMS(arr)[0]; i++) + { + switch (kinds[i]) + { + case STATS_EXT_NDISTINCT: + enabled->ndistinct = true; + break; + case STATS_EXT_DEPENDENCIES: + enabled->dependencies = true; + break; + case STATS_EXT_MCV: + enabled->mcv = true; + break; + case STATS_EXT_EXPRESSIONS: + enabled->expressions = true; + break; + default: + elog(ERROR, "incorrect stxkind %c found", kinds[i]); + break; + } + } +} + +/* + * Perform the actual storage of a pg_statistic_ext_data tuple. + */ +static void +upsert_pg_statistic_ext_data(const Datum *values, const bool *nulls, + const bool *replaces) +{ + Relation pg_stextdata; + HeapTuple stxdtup; + HeapTuple newtup; + + pg_stextdata = table_open(StatisticExtDataRelationId, RowExclusiveLock); + + stxdtup = SearchSysCache2(STATEXTDATASTXOID, + values[Anum_pg_statistic_ext_data_stxoid - 1], + values[Anum_pg_statistic_ext_data_stxdinherit - 1]); + + if (HeapTupleIsValid(stxdtup)) + { + newtup = heap_modify_tuple(stxdtup, + RelationGetDescr(pg_stextdata), + values, + nulls, + replaces); + CatalogTupleUpdate(pg_stextdata, &newtup->t_self, newtup); + ReleaseSysCache(stxdtup); + } + else + { + newtup = heap_form_tuple(RelationGetDescr(pg_stextdata), values, nulls); + CatalogTupleInsert(pg_stextdata, newtup); + } + + heap_freetuple(newtup); + + CommandCounterIncrement(); + + table_close(pg_stextdata, RowExclusiveLock); +} + +/* + * Insert or update an extended statistics object. + * + * Major errors, such as the table not existing or permission errors, are + * reported as ERRORs. There are a couple of paths that generate a WARNING, + * like when the statistics object or its schema do not exist, a conversion + * failure on one statistic kind, or when other statistic kinds may still + * be updated. + */ +static bool +extended_statistics_update(FunctionCallInfo fcinfo) +{ + char *relnspname; + char *relname; + Oid nspoid; + char *nspname; + char *stxname; + bool inherited; + Relation pg_stext = NULL; + HeapTuple tup = NULL; + + StakindFlags enabled = {false, false, false, false}; + StakindFlags has = {false, false, false, false}; + + Form_pg_statistic_ext stxform; + + Datum values[Natts_pg_statistic_ext_data] = {0}; + bool nulls[Natts_pg_statistic_ext_data]; + bool replaces[Natts_pg_statistic_ext_data] = {0}; + bool success = true; + int numexprs = 0; + + /* arrays of type info, if we need them */ + Oid relid; + Oid locked_table = InvalidOid; + + /* + * Fill out the StakindFlags "has" structure based on which parameters + * were provided to the function. + */ + has.ndistinct = !PG_ARGISNULL(NDISTINCT_ARG); + + if (RecoveryInProgress()) + { + ereport(WARNING, + errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("recovery is in progress"), + errhint("Statistics cannot be modified during recovery.")); + return false; + } + + /* relation arguments */ + stats_check_required_arg(fcinfo, extarginfo, RELSCHEMA_ARG); + relnspname = TextDatumGetCString(PG_GETARG_DATUM(RELSCHEMA_ARG)); + stats_check_required_arg(fcinfo, extarginfo, RELNAME_ARG); + relname = TextDatumGetCString(PG_GETARG_DATUM(RELNAME_ARG)); + + /* extended statistics arguments */ + stats_check_required_arg(fcinfo, extarginfo, STATSCHEMA_ARG); + nspname = TextDatumGetCString(PG_GETARG_DATUM(STATSCHEMA_ARG)); + stats_check_required_arg(fcinfo, extarginfo, STATNAME_ARG); + stxname = TextDatumGetCString(PG_GETARG_DATUM(STATNAME_ARG)); + stats_check_required_arg(fcinfo, extarginfo, INHERITED_ARG); + inherited = PG_GETARG_BOOL(INHERITED_ARG); + + /* + * First open the relation where we expect to find the statistics. This + * is similar to relation and attribute statistics, so as ACL checks are + * done before any locks are taken, even before any attempts related to + * the extended stats object. + */ + relid = RangeVarGetRelidExtended(makeRangeVar(relnspname, relname, -1), + ShareUpdateExclusiveLock, 0, + RangeVarCallbackForStats, &locked_table); + + nspoid = get_namespace_oid(nspname, true); + if (nspoid == InvalidOid) + { + ereport(WARNING, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find schema \"%s\"", nspname)); + success = false; + goto cleanup; + } + + pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock); + tup = get_pg_statistic_ext(pg_stext, nspoid, stxname); + + if (!HeapTupleIsValid(tup)) + { + ereport(WARNING, + errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find extended statistics object \"%s\".\"%s\"", + quote_identifier(nspname), + quote_identifier(stxname))); + success = false; + goto cleanup; + } + + stxform = (Form_pg_statistic_ext) GETSTRUCT(tup); + + /* + * The relation tracked by the stats object has to match with the relation + * we have already locked. + */ + if (stxform->stxrelid != relid) + { + ereport(WARNING, + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("could not restore extended statistics object \"%s\".\"%s\": incorrect relation \"%s\".\"%s\" specified", + quote_identifier(nspname), + quote_identifier(stxname), + quote_identifier(relnspname), + quote_identifier(relname))); + + success = false; + goto cleanup; + } + + /* Find out what extended statistics kinds we should expect. */ + expand_stxkind(tup, &enabled); + + /* + * If the object cannot support ndistinct, we should not have data for it. + */ + if (has.ndistinct && !enabled.ndistinct) + { + ereport(WARNING, + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("cannot not specify parameter \"%s\"", + extarginfo[NDISTINCT_ARG].argname), + errhint("Extended statistics object \"%s\".\"%s\" does not support statistics of this type.", + quote_identifier(nspname), + quote_identifier(stxname))); + + has.ndistinct = false; + success = false; + } + + /* + * Populate the pg_statistic_ext_data result tuple. + */ + + /* Primary Key: cannot be NULL or replaced. */ + values[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(stxform->oid); + values[Anum_pg_statistic_ext_data_stxdinherit - 1] = BoolGetDatum(inherited); + + /* All unspecified parameters will be left unmodified */ + nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true; + nulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true; + nulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true; + nulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true; + + /* + * For each stats kind, deserialize the data at hand and perform a round + * of validation. The resulting tuple is filled with a set of updated + * values. + */ + + if (has.ndistinct) + { + Datum ndistinct_datum = PG_GETARG_DATUM(NDISTINCT_ARG); + bytea *data = DatumGetByteaPP(ndistinct_datum); + MVNDistinct *ndistinct = statext_ndistinct_deserialize(data); + + if (statext_ndistinct_validate(ndistinct, &stxform->stxkeys, + numexprs, WARNING)) + { + values[Anum_pg_statistic_ext_data_stxdndistinct - 1] = ndistinct_datum; + nulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = false; + replaces[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true; + } + else + success = false; + + statext_ndistinct_free(ndistinct); + } + + upsert_pg_statistic_ext_data(values, nulls, replaces); + +cleanup: + if (HeapTupleIsValid(tup)) + heap_freetuple(tup); + if (pg_stext != NULL) + table_close(pg_stext, RowExclusiveLock); + return success; +} + /* * Remove an existing pg_statistic_ext_data row for a given pg_statistic_ext * row and "inherited" pair. @@ -139,6 +434,31 @@ delete_pg_statistic_ext_data(Oid stxoid, bool inherited) return result; } +/* + * Restore (insert or replace) statistics for the given statistics object. + * + * This function accepts variadic arguments in key-value pairs, which are + * given to stats_fill_fcinfo_from_arg_pairs to be mapped into positional + * arguments. + */ +Datum +pg_restore_extended_stats(PG_FUNCTION_ARGS) +{ + LOCAL_FCINFO(positional_fcinfo, NUM_EXTENDED_STATS_ARGS); + bool result = true; + + InitFunctionCallInfoData(*positional_fcinfo, NULL, NUM_EXTENDED_STATS_ARGS, + InvalidOid, NULL, NULL); + + if (!stats_fill_fcinfo_from_arg_pairs(fcinfo, positional_fcinfo, extarginfo)) + result = false; + + if (!extended_statistics_update(positional_fcinfo)) + result = false; + + PG_RETURN_BOOL(result); +} + /* * Delete statistics for the given statistics object. */ diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 79db8731621..fb577026666 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202601221 +#define CATALOG_VERSION_NO 202601261 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 894b6a1b6d6..5e5e33f64fc 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -12619,6 +12619,11 @@ proargtypes => 'int4', prosrc => 'gist_translate_cmptype_common' }, # Extended Statistics functions +{ oid => '9947', descr => 'restore statistics on extended statistics object', + proname => 'pg_restore_extended_stats', provariadic => 'any', + proisstrict => 'f', provolatile => 'v', proparallel => 'u', + prorettype => 'bool', proargtypes => 'any', proargmodes => '{v}', + proargnames => '{kwargs}', prosrc => 'pg_restore_extended_stats' }, { oid => '9948', descr => 'clear statistics on extended statistics object', proname => 'pg_clear_extended_stats', proisstrict => 'f', provolatile => 'v', proparallel => 'u', prorettype => 'void', proargtypes => 'text text text text bool', diff --git a/src/test/regress/expected/stats_import.out b/src/test/regress/expected/stats_import.out index 3fd77879e13..acab1367855 100644 --- a/src/test/regress/expected/stats_import.out +++ b/src/test/regress/expected/stats_import.out @@ -1141,6 +1141,12 @@ CREATE INDEX is_odd ON stats_import.test(((comp).a % 2 = 1)); CREATE STATISTICS stats_import.test_stat ON name, comp, lower(arange), array_length(tags,1) FROM stats_import.test; +CREATE STATISTICS stats_import.test_stat_ndistinct (ndistinct) + ON name, comp + FROM stats_import.test; +CREATE STATISTICS stats_import.test_stat_dependencies (dependencies) + ON name, comp + FROM stats_import.test; -- Generate statistics on table with data ANALYZE stats_import.test; CREATE TABLE stats_import.test_clone ( LIKE stats_import.test ) @@ -1590,6 +1596,147 @@ RESET ROLE; REVOKE MAINTAIN ON stats_import.test FROM regress_test_extstat_clear; REVOKE ALL ON SCHEMA stats_import FROM regress_test_extstat_clear; DROP ROLE regress_test_extstat_clear; +-- Tests for pg_restore_extended_stats(). +-- Invalid argument values. +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', NULL, + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +ERROR: argument "schemaname" must not be null +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', NULL, + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +ERROR: argument "relname" must not be null +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', NULL, + 'statistics_name', 'test_stat_clone', + 'inherited', false); +ERROR: argument "statistics_schemaname" must not be null +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', NULL, + 'inherited', false); +ERROR: argument "statistics_name" must not be null +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', NULL); +ERROR: argument "inherited" must not be null +-- Missing objects +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'schema_not_exist', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +ERROR: schema "schema_not_exist" does not exist +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'table_not_exist', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +ERROR: relation "stats_import.table_not_exist" does not exist +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'schema_not_exist', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +WARNING: could not find schema "schema_not_exist" + pg_restore_extended_stats +--------------------------- + f +(1 row) + +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'ext_stats_not_exist', + 'inherited', false); +WARNING: could not find extended statistics object "stats_import"."ext_stats_not_exist" + pg_restore_extended_stats +--------------------------- + f +(1 row) + +-- Incorrect relation/extended stats combination +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +WARNING: could not restore extended statistics object "stats_import"."test_stat_clone": incorrect relation "stats_import"."test" specified + pg_restore_extended_stats +--------------------------- + f +(1 row) + +-- ndistinct value doesn't match object definition +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_ndistinct', + 'inherited', false, + 'n_distinct', '[{"attributes" : [1,3], "ndistinct" : 4}]'::pg_ndistinct); +WARNING: could not validate "pg_ndistinct" object: invalid attribute number 1 found + pg_restore_extended_stats +--------------------------- + f +(1 row) + +-- Incorrect extended stats kind, ndistinct not supported +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_dependencies', + 'inherited', false, + 'n_distinct', '[{"attributes" : [1,3], "ndistinct" : 4}]'::pg_ndistinct); +WARNING: cannot not specify parameter "n_distinct" +HINT: Extended statistics object "stats_import"."test_stat_dependencies" does not support statistics of this type. + pg_restore_extended_stats +--------------------------- + f +(1 row) + +-- ok: ndistinct +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_ndistinct', + 'inherited', false, + 'n_distinct', '[{"attributes" : [2,3], "ndistinct" : 4}]'::pg_ndistinct); + pg_restore_extended_stats +--------------------------- + t +(1 row) + +SELECT replace(e.n_distinct, '}, ', E'},\n') AS n_distinct +FROM pg_stats_ext AS e +WHERE e.statistics_schemaname = 'stats_import' AND + e.statistics_name = 'test_stat_ndistinct' AND + e.inherited = false; + n_distinct +------------------------------------------ + [{"attributes": [2, 3], "ndistinct": 4}] +(1 row) + DROP SCHEMA stats_import CASCADE; NOTICE: drop cascades to 7 other objects DETAIL: drop cascades to type stats_import.complex_type diff --git a/src/test/regress/sql/stats_import.sql b/src/test/regress/sql/stats_import.sql index c2d927203d5..5d35de1bc88 100644 --- a/src/test/regress/sql/stats_import.sql +++ b/src/test/regress/sql/stats_import.sql @@ -811,6 +811,14 @@ CREATE STATISTICS stats_import.test_stat ON name, comp, lower(arange), array_length(tags,1) FROM stats_import.test; +CREATE STATISTICS stats_import.test_stat_ndistinct (ndistinct) + ON name, comp + FROM stats_import.test; + +CREATE STATISTICS stats_import.test_stat_dependencies (dependencies) + ON name, comp + FROM stats_import.test; + -- Generate statistics on table with data ANALYZE stats_import.test; @@ -1137,4 +1145,101 @@ REVOKE MAINTAIN ON stats_import.test FROM regress_test_extstat_clear; REVOKE ALL ON SCHEMA stats_import FROM regress_test_extstat_clear; DROP ROLE regress_test_extstat_clear; +-- Tests for pg_restore_extended_stats(). +-- Invalid argument values. +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', NULL, + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', NULL, + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', NULL, + 'statistics_name', 'test_stat_clone', + 'inherited', false); +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', NULL, + 'inherited', false); +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', NULL); +-- Missing objects +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'schema_not_exist', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'table_not_exist', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'schema_not_exist', + 'statistics_name', 'test_stat_clone', + 'inherited', false); +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test_clone', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'ext_stats_not_exist', + 'inherited', false); +-- Incorrect relation/extended stats combination +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_clone', + 'inherited', false); + +-- ndistinct value doesn't match object definition +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_ndistinct', + 'inherited', false, + 'n_distinct', '[{"attributes" : [1,3], "ndistinct" : 4}]'::pg_ndistinct); +-- Incorrect extended stats kind, ndistinct not supported +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_dependencies', + 'inherited', false, + 'n_distinct', '[{"attributes" : [1,3], "ndistinct" : 4}]'::pg_ndistinct); + +-- ok: ndistinct +SELECT pg_catalog.pg_restore_extended_stats( + 'schemaname', 'stats_import', + 'relname', 'test', + 'statistics_schemaname', 'stats_import', + 'statistics_name', 'test_stat_ndistinct', + 'inherited', false, + 'n_distinct', '[{"attributes" : [2,3], "ndistinct" : 4}]'::pg_ndistinct); + +SELECT replace(e.n_distinct, '}, ', E'},\n') AS n_distinct +FROM pg_stats_ext AS e +WHERE e.statistics_schemaname = 'stats_import' AND + e.statistics_name = 'test_stat_ndistinct' AND + e.inherited = false; + DROP SCHEMA stats_import CASCADE; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 1c8610fd46c..ddbe4c64971 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -2897,6 +2897,7 @@ SplitPoint SplitTextOutputData SplitVar StackElem +StakindFlags StartDataPtrType StartLOPtrType StartLOsPtrType