*** a/doc/src/sgml/perform.sgml --- b/doc/src/sgml/perform.sgml *************** *** 1132,1138 **** WHERE tablename = 'road'; To inspect functional dependencies on a statistics stts, you may do this: ! CREATE STATISTICS stts WITH (dependencies) ON (zip, city) FROM zipcodes; ANALYZE zipcodes; SELECT stxname, stxkeys, stxdependencies --- 1132,1138 ---- To inspect functional dependencies on a statistics stts, you may do this: ! CREATE STATISTICS stts (dependencies) ON (zip, city) FROM zipcodes; ANALYZE zipcodes; SELECT stxname, stxkeys, stxdependencies *************** *** 1219,1225 **** EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 10; Continuing the above example, the n-distinct coefficients in a ZIP code table may look like the following: ! CREATE STATISTICS stts2 WITH (ndistinct) ON (zip, state, city) FROM zipcodes; ANALYZE zipcodes; SELECT stxkeys AS k, stxndistinct AS nd --- 1219,1225 ---- Continuing the above example, the n-distinct coefficients in a ZIP code table may look like the following: ! CREATE STATISTICS stts2 (ndistinct) ON (zip, state, city) FROM zipcodes; ANALYZE zipcodes; SELECT stxkeys AS k, stxndistinct AS nd *** a/doc/src/sgml/planstats.sgml --- b/doc/src/sgml/planstats.sgml *************** *** 526,532 **** EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1; multivariate statistics on the two columns: ! CREATE STATISTICS stts WITH (dependencies) ON (a, b) FROM t; ANALYZE t; EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1; QUERY PLAN --- 526,532 ---- multivariate statistics on the two columns: ! CREATE STATISTICS stts (dependencies) ON (a, b) FROM t; ANALYZE t; EXPLAIN (ANALYZE, TIMING OFF) SELECT * FROM t WHERE a = 1 AND b = 1; QUERY PLAN *************** *** 569,575 **** EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b; calculation, the estimate is much improved: DROP STATISTICS stts; ! CREATE STATISTICS stts WITH (dependencies, ndistinct) ON (a, b) FROM t; ANALYZE t; EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b; QUERY PLAN --- 569,575 ---- calculation, the estimate is much improved: DROP STATISTICS stts; ! CREATE STATISTICS stts (dependencies, ndistinct) ON (a, b) FROM t; ANALYZE t; EXPLAIN (ANALYZE, TIMING OFF) SELECT COUNT(*) FROM t GROUP BY a, b; QUERY PLAN *** a/doc/src/sgml/ref/create_statistics.sgml --- b/doc/src/sgml/ref/create_statistics.sgml *************** *** 22,29 **** PostgreSQL documentation CREATE STATISTICS [ IF NOT EXISTS ] statistics_name ! WITH ( option [= value] [, ... ] ) ! ON ( column_name, column_name [, ...]) FROM table_name --- 22,29 ---- CREATE STATISTICS [ IF NOT EXISTS ] statistics_name ! [ ( statistic_type [, ... ] ) ] ! FOR ( column_name, column_name [, ...]) FROM table_name *************** *** 75,80 **** CREATE STATISTICS [ IF NOT EXISTS ] statistics_na --- 75,93 ---- + statistic_type + + + A statistic type to be enabled for this statistics. Currently + supported types are ndistinct, which enables + n-distinct coefficient tracking, + and dependencies, which enables functional + dependencies. + + + + + column_name *************** *** 94,135 **** CREATE STATISTICS [ IF NOT EXISTS ] statistics_na - - - Parameters - - - statistics parameters - - - - The WITH clause can specify options - for the statistics. Available options are listed below. - - - - - - dependencies (boolean) - - - Enables functional dependencies for the statistics. - - - - - - ndistinct (boolean) - - - Enables ndistinct coefficients for the statistics. - - - - - - - --- 107,112 ---- *** a/src/backend/commands/statscmds.c --- b/src/backend/commands/statscmds.c *************** *** 199,223 **** CreateStatistics(CreateStatsStmt *stmt) */ build_ndistinct = false; build_dependencies = false; ! foreach(l, stmt->options) { ! DefElem *opt = (DefElem *) lfirst(l); ! if (strcmp(opt->defname, "ndistinct") == 0) { ! build_ndistinct = defGetBoolean(opt); requested_type = true; } ! else if (strcmp(opt->defname, "dependencies") == 0) { ! build_dependencies = defGetBoolean(opt); requested_type = true; } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("unrecognized STATISTICS option \"%s\"", ! opt->defname))); } /* If no statistic type was specified, build them all. */ if (!requested_type) --- 199,223 ---- */ build_ndistinct = false; build_dependencies = false; ! foreach(l, stmt->stat_types) { ! char *type = strVal((Value *) lfirst(l)); ! if (strcmp(type, "ndistinct") == 0) { ! build_ndistinct = true; requested_type = true; } ! else if (strcmp(type, "dependencies") == 0) { ! build_dependencies = true; requested_type = true; } else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("unrecognized statistics type \"%s\"", ! type))); } /* If no statistic type was specified, build them all. */ if (!requested_type) *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 3397,3402 **** _copyCreateStatsStmt(const CreateStatsStmt *from) --- 3397,3413 ---- return newnode; } + static CreateStatsArgument * + _copyCreateStatsArgument(const CreateStatsArgument *from) + { + CreateStatsArgument *newnode = makeNode(CreateStatsArgument); + + COPY_SCALAR_FIELD(subtype); + COPY_NODE_FIELD(elements); + + return newnode; + } + static CreateFunctionStmt * _copyCreateFunctionStmt(const CreateFunctionStmt *from) { *************** *** 5121,5126 **** copyObjectImpl(const void *from) --- 5132,5140 ---- case T_CreateStatsStmt: retval = _copyCreateStatsStmt(from); break; + case T_CreateStatsArgument: + retval = _copyCreateStatsArgument(from); + break; case T_CreateFunctionStmt: retval = _copyCreateFunctionStmt(from); break; *** a/src/backend/nodes/equalfuncs.c --- b/src/backend/nodes/equalfuncs.c *************** *** 1358,1363 **** _equalCreateStatsStmt(const CreateStatsStmt *a, const CreateStatsStmt *b) --- 1358,1372 ---- } static bool + _equalCreateStatsArgument(const CreateStatsArgument *a, const CreateStatsArgument *b) + { + COMPARE_SCALAR_FIELD(subtype); + COMPARE_NODE_FIELD(elements); + + return true; + } + + static bool _equalCreateFunctionStmt(const CreateFunctionStmt *a, const CreateFunctionStmt *b) { COMPARE_SCALAR_FIELD(replace); *************** *** 3270,3275 **** equal(const void *a, const void *b) --- 3279,3287 ---- case T_CreateStatsStmt: retval = _equalCreateStatsStmt(a, b); break; + case T_CreateStatsArgument: + retval = _equalCreateStatsArgument(a, b); + break; case T_CreateFunctionStmt: retval = _equalCreateFunctionStmt(a, b); break; *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** *** 2646,2651 **** _outCreateStatsStmt(StringInfo str, const CreateStatsStmt *node) --- 2646,2660 ---- } static void + _outCreateStatsArgument(StringInfo str, const CreateStatsArgument *node) + { + WRITE_NODE_TYPE("CREATESTATSARG"); + + WRITE_INT_FIELD(subtype); + WRITE_NODE_FIELD(elements); + } + + static void _outNotifyStmt(StringInfo str, const NotifyStmt *node) { WRITE_NODE_TYPE("NOTIFY"); *************** *** 4051,4056 **** outNode(StringInfo str, const void *obj) --- 4060,4068 ---- case T_CreateStatsStmt: _outCreateStatsStmt(str, obj); break; + case T_CreateStatsArgument: + _outCreateStatsArgument(str, obj); + break; case T_NotifyStmt: _outNotifyStmt(str, obj); break; *** a/src/backend/parser/gram.y --- b/src/backend/parser/gram.y *************** *** 183,188 **** static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_ --- 183,189 ---- static void SplitColQualList(List *qualList, List **constraintList, CollateClause **collClause, core_yyscan_t yyscanner); + static void SplitStatsArgList(CreateStatsStmt *stmt, List *arguments); static void processCASbits(int cas_bits, int location, const char *constrType, bool *deferrable, bool *initdeferred, bool *not_valid, bool *no_inherit, core_yyscan_t yyscanner); *************** *** 236,241 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); --- 237,243 ---- AccessPriv *accesspriv; struct ImportQual *importqual; InsertStmt *istmt; + CreateStatsArgument *cstatarg; VariableSetStmt *vsetstmt; PartitionElem *partelem; PartitionSpec *partspec; *************** *** 397,402 **** static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); --- 399,406 ---- transform_element_list transform_type_list TriggerTransitions TriggerReferencing publication_name_list + opt_stats_type_list stats_type_list StatisticArgList + %type StatisticArgument %type group_by_list %type group_by_item empty_grouping_set rollup_clause cube_clause *************** *** 3836,3868 **** ExistingIndex: USING INDEX index_name { $$ = $3; } /***************************************************************************** * * QUERY : ! * CREATE STATISTICS stats_name WITH (options) ON (columns) FROM relname * *****************************************************************************/ ! CreateStatsStmt: CREATE STATISTICS any_name opt_reloptions ON '(' columnList ')' FROM qualified_name ! { ! CreateStatsStmt *n = makeNode(CreateStatsStmt); ! n->defnames = $3; ! n->relation = $10; ! n->keys = $7; ! n->options = $4; ! n->if_not_exists = false; ! $$ = (Node *)n; ! } ! | CREATE STATISTICS IF_P NOT EXISTS any_name opt_reloptions ON '(' columnList ')' FROM qualified_name ! { ! CreateStatsStmt *n = makeNode(CreateStatsStmt); ! n->defnames = $6; ! n->relation = $13; ! n->keys = $10; ! n->options = $7; ! n->if_not_exists = true; ! $$ = (Node *)n; ! } ; /***************************************************************************** * * QUERY : --- 3840,3913 ---- /***************************************************************************** * * QUERY : ! * CREATE STATISTICS stats_name [(stat types)] arguments ! ! * where 'arguments' can be one or more of: ! * { FOR (columns) ! * | FROM relations ! * | WITH (options) ! * | WHERE expression } * *****************************************************************************/ + CreateStatsStmt: + CREATE opt_if_not_exists STATISTICS any_name + opt_stats_type_list StatisticArgList + { + CreateStatsStmt *n = makeNode(CreateStatsStmt); + n->defnames = $4; + n->stat_types = $5; + n->if_not_exists = $2; ! SplitStatsArgList(n, $6); ! $$ = (Node *)n; ! } ; + opt_stats_type_list: + '(' stats_type_list ')' { $$ = $2; } + | /* EMPTY */ { $$ = NULL; } + ; + + stats_type_list: + ColId { $$ = list_make1(makeString($1)); } + | stats_type_list ',' ColId { $$ = lappend($1, makeString($3)); } + ; + + StatisticArgList: + StatisticArgument { $$ = list_make1($1); } + | StatisticArgList StatisticArgument { $$ = lappend($1, $2); } + ; + + StatisticArgument: + FOR '(' name_list ')' + { + CreateStatsArgument *n = makeNode(CreateStatsArgument); + n->subtype = CSA_Expressions; + n->elements = $3; + $$ = n; + } + | FROM qualified_name_list + { + CreateStatsArgument *n = makeNode(CreateStatsArgument); + n->subtype = CSA_Relations; + n->elements = $2; + $$ = n; + } + | WITH reloptions + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WITH clause is not yet implemented"))); + } + | WHERE a_expr + { + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("WHERE clause is not yet implemented"))); + } + ; + /***************************************************************************** * * QUERY : *************** *** 15871,15876 **** processCASbits(int cas_bits, int location, const char *constrType, --- 15916,15968 ---- } } + /* + * Split out CREATE STATISTICS arguments. + */ + static void + SplitStatsArgList(CreateStatsStmt *stmt, List *arguments) + { + ListCell *cell; + + foreach(cell, arguments) + { + CreateStatsArgument *n = lfirst_node(CreateStatsArgument, cell); + + switch (n->subtype) + { + case CSA_Relations: + if (stmt->relation) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("redundant or conflicting FROM clauses"))); + if (list_length(n->elements) > 1) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("statistics across multiple relations are not supported yet"))); + stmt->relation = linitial_node(RangeVar, n->elements); + break; + case CSA_Expressions: + if (stmt->keys) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("redundant or conflicting ON clauses"))); + stmt->keys = n->elements; + break; + default: + elog(ERROR, "unsupported node type %d", n->subtype); + } + } + + if (!stmt->relation) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("missing FROM clause"))); + if (!stmt->keys) + ereport(ERROR, + (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), + errmsg("missing FOR clause"))); + } + /*---------- * Recursive view transformation * *** a/src/backend/utils/adt/ruleutils.c --- b/src/backend/utils/adt/ruleutils.c *************** *** 1504,1518 **** pg_get_statisticsext_worker(Oid statextid, bool missing_ok) } /* ! * If any option is disabled, then we'll need to append a WITH clause to ! * show which options are enabled. We omit the WITH clause on purpose * when all options are enabled, so a pg_dump/pg_restore will create all * statistics types on a newer postgres version, if the statistics had all * options enabled on the original version. */ if (!ndistinct_enabled || !dependencies_enabled) { ! appendStringInfoString(&buf, " WITH ("); if (ndistinct_enabled) appendStringInfoString(&buf, "ndistinct"); else if (dependencies_enabled) --- 1504,1518 ---- } /* ! * If any option is disabled, then we'll need to append the types clause ! * to show which options are enabled. We omit the types clause on purpose * when all options are enabled, so a pg_dump/pg_restore will create all * statistics types on a newer postgres version, if the statistics had all * options enabled on the original version. */ if (!ndistinct_enabled || !dependencies_enabled) { ! appendStringInfoString(&buf, " ("); if (ndistinct_enabled) appendStringInfoString(&buf, "ndistinct"); else if (dependencies_enabled) *************** *** 1521,1527 **** pg_get_statisticsext_worker(Oid statextid, bool missing_ok) appendStringInfoChar(&buf, ')'); } ! appendStringInfoString(&buf, " ON ("); for (colno = 0; colno < statextrec->stxkeys.dim1; colno++) { --- 1521,1527 ---- appendStringInfoChar(&buf, ')'); } ! appendStringInfoString(&buf, " FOR ("); for (colno = 0; colno < statextrec->stxkeys.dim1; colno++) { *** a/src/bin/pg_dump/t/002_pg_dump.pl --- b/src/bin/pg_dump/t/002_pg_dump.pl *************** *** 4990,4999 **** qr/CREATE TRANSFORM FOR integer LANGUAGE sql \(FROM SQL WITH FUNCTION pg_catalog all_runs => 1, catch_all => 'CREATE ... commands', create_order => 97, ! create_sql => 'CREATE STATISTICS dump_test.test_ext_stats_using ! WITH (ndistinct) ON (col1, col2) FROM dump_test.test_fifth_table', regexp => qr/^ ! \QCREATE STATISTICS dump_test.test_ext_stats_using WITH (ndistinct) ON (col1, col2) FROM test_fifth_table;\E /xms, like => { binary_upgrade => 1, --- 4990,4999 ---- all_runs => 1, catch_all => 'CREATE ... commands', create_order => 97, ! create_sql => 'CREATE STATISTICS dump_test.test_ext_stats_opts ! (ndistinct) ON (col1, col2) FROM dump_test.test_fifth_table', regexp => qr/^ ! \QCREATE STATISTICS dump_test.test_ext_stats_opts (ndistinct) ON (col1, col2) FROM test_fifth_table;\E /xms, like => { binary_upgrade => 1, *** a/src/bin/psql/describe.c --- b/src/bin/psql/describe.c *************** *** 2385,2391 **** describeOneTableDetails(const char *schemaname, printfPQExpBuffer(&buf, " "); /* statistics name (qualified with namespace) */ ! appendPQExpBuffer(&buf, "\"%s.%s\" WITH (", PQgetvalue(result, i, 1), PQgetvalue(result, i, 2)); --- 2385,2391 ---- printfPQExpBuffer(&buf, " "); /* statistics name (qualified with namespace) */ ! appendPQExpBuffer(&buf, "\"%s.%s\" (", PQgetvalue(result, i, 1), PQgetvalue(result, i, 2)); *************** *** 2401,2407 **** describeOneTableDetails(const char *schemaname, appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : ""); } ! appendPQExpBuffer(&buf, ") ON (%s)", PQgetvalue(result, i, 4)); printTableAddFooter(&cont, buf.data); --- 2401,2407 ---- appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : ""); } ! appendPQExpBuffer(&buf, ") FOR (%s)", PQgetvalue(result, i, 4)); printTableAddFooter(&cont, buf.data); *** a/src/bin/psql/tab-complete.c --- b/src/bin/psql/tab-complete.c *************** *** 453,458 **** static const SchemaQuery Query_for_list_of_foreign_tables = { --- 453,473 ---- NULL }; + static const SchemaQuery Query_for_list_of_statistics = { + /* catname */ + "pg_catalog.pg_statistic_ext s", + /* selcondition */ + NULL, + /* viscondition */ + NULL, + /* namespace */ + "s.stxnamespace", + /* result */ + "pg_catalog.quote_ident(s.stxname)", + /* qualresult */ + NULL + }; + static const SchemaQuery Query_for_list_of_tables = { /* catname */ "pg_catalog.pg_class c", *************** *** 1023,1028 **** static const pgsql_thing_t words_after_create[] = { --- 1038,1044 ---- {"SCHEMA", Query_for_list_of_schemas}, {"SEQUENCE", NULL, &Query_for_list_of_sequences}, {"SERVER", Query_for_list_of_servers}, + {"STATISTICS", NULL, &Query_for_list_of_statistics}, {"SUBSCRIPTION", Query_for_list_of_subscriptions}, {"SYSTEM", NULL, NULL, THING_NO_CREATE | THING_NO_DROP}, {"TABLE", NULL, &Query_for_list_of_tables}, *************** *** 1386,1391 **** psql_completion(const char *text, int start, int end) --- 1402,1414 ---- word_matches(p2, previous_words[previous_words_count - 2]) && \ word_matches(p3, previous_words[previous_words_count - 3])) + #define HeadMatches4(p1, p2, p3, p4) \ + (previous_words_count >= 4 && \ + word_matches(p1, previous_words[previous_words_count - 1]) && \ + word_matches(p2, previous_words[previous_words_count - 2]) && \ + word_matches(p3, previous_words[previous_words_count - 3]) && \ + word_matches(p4, previous_words[previous_words_count - 4])) + /* Known command-starting keywords. */ static const char *const sql_commands[] = { "ABORT", "ALTER", "ANALYZE", "BEGIN", "CHECKPOINT", "CLOSE", "CLUSTER", *************** *** 1783,1788 **** psql_completion(const char *text, int start, int end) --- 1806,1815 ---- else if (Matches5("ALTER", "RULE", MatchAny, "ON", MatchAny)) COMPLETE_WITH_CONST("RENAME TO"); + /* ALTER STATISTICS */ + else if (Matches3("ALTER", "STATISTICS", MatchAny)) + COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA"); + /* ALTER TRIGGER , add ON */ else if (Matches3("ALTER", "TRIGGER", MatchAny)) COMPLETE_WITH_CONST("ON"); *************** *** 2119,2125 **** psql_completion(const char *text, int start, int end) {"ACCESS METHOD", "CAST", "COLLATION", "CONVERSION", "DATABASE", "EVENT TRIGGER", "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", ! "SERVER", "INDEX", "LANGUAGE", "POLICY", "PUBLICATION", "RULE", "SCHEMA", "SEQUENCE", "SUBSCRIPTION", "TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", "TABLESPACE", "TEXT SEARCH", "ROLE", NULL}; --- 2146,2153 ---- {"ACCESS METHOD", "CAST", "COLLATION", "CONVERSION", "DATABASE", "EVENT TRIGGER", "EXTENSION", "FOREIGN DATA WRAPPER", "FOREIGN TABLE", ! "SERVER", "INDEX", "LANGUAGE", "POLICY", "PUBLICATION", "RULE", ! "SCHEMA", "SEQUENCE", "STATISTICS", "SUBSCRIPTION", "TABLE", "TYPE", "VIEW", "MATERIALIZED VIEW", "COLUMN", "AGGREGATE", "FUNCTION", "OPERATOR", "TRIGGER", "CONSTRAINT", "DOMAIN", "LARGE OBJECT", "TABLESPACE", "TEXT SEARCH", "ROLE", NULL}; *************** *** 2383,2388 **** psql_completion(const char *text, int start, int end) --- 2411,2428 ---- else if (Matches3("CREATE", "SERVER", MatchAny)) COMPLETE_WITH_LIST3("TYPE", "VERSION", "FOREIGN DATA WRAPPER"); + /* CREATE STATISTICS */ + else if (Matches3("CREATE", "STATISTICS", MatchAny)) + COMPLETE_WITH_LIST3("(", "FOR", "FROM"); + else if (Matches4("CREATE", "STATISTICS", MatchAny, "(")) + COMPLETE_WITH_LIST2("ndistinct", "dependencies"); + else if (HeadMatches3("CREATE", "STATISTICS", MatchAny) && + TailMatches1("FOR")) + COMPLETE_WITH_CONST("("); + else if (HeadMatches3("CREATE", "STATISTICS", MatchAny) && + TailMatches1("FROM")) + COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_tables, NULL); + /* CREATE TABLE --- is allowed inside CREATE SCHEMA, so use TailMatches */ /* Complete "CREATE TEMP/TEMPORARY" with the possible temp objects */ else if (TailMatches2("CREATE", "TEMP|TEMPORARY")) *************** *** 2589,2595 **** psql_completion(const char *text, int start, int end) /* DROP */ /* Complete DROP object with CASCADE / RESTRICT */ else if (Matches3("DROP", ! "COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|TABLE|TYPE|VIEW", MatchAny) || Matches4("DROP", "ACCESS", "METHOD", MatchAny) || (Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) && --- 2629,2635 ---- /* DROP */ /* Complete DROP object with CASCADE / RESTRICT */ else if (Matches3("DROP", ! "COLLATION|CONVERSION|DOMAIN|EXTENSION|LANGUAGE|PUBLICATION|SCHEMA|SEQUENCE|SERVER|SUBSCRIPTION|STATISTICS|TABLE|TYPE|VIEW", MatchAny) || Matches4("DROP", "ACCESS", "METHOD", MatchAny) || (Matches4("DROP", "AGGREGATE|FUNCTION", MatchAny, MatchAny) && *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** *** 462,467 **** typedef enum NodeTag --- 462,468 ---- T_InferClause, T_OnConflictClause, T_CommonTableExpr, + T_CreateStatsArgument, T_RoleSpec, T_TriggerTransition, T_PartitionElem, *** a/src/include/nodes/parsenodes.h --- b/src/include/nodes/parsenodes.h *************** *** 2689,2700 **** typedef struct CreateStatsStmt --- 2689,2714 ---- { NodeTag type; List *defnames; /* qualified name (list of Value strings) */ + List *stat_types; /* stat types (list of Value strings) */ RangeVar *relation; /* relation to build statistics on */ List *keys; /* String nodes naming referenced columns */ List *options; /* list of DefElem */ bool if_not_exists; /* do nothing if statistics already exists */ } CreateStatsStmt; + typedef enum CSA_Type + { + CSA_Relations, + CSA_Expressions + } CSA_Type; + + typedef struct CreateStatsArgument + { + NodeTag type; + CSA_Type subtype; + List *elements; /* elements (list of Node) */ + } CreateStatsArgument; + /* ---------------------- * Create Function Statement * ---------------------- *** a/src/test/regress/expected/alter_generic.out --- b/src/test/regress/expected/alter_generic.out *************** *** 501,508 **** DROP OPERATOR FAMILY alt_opf18 USING btree; -- SET SESSION AUTHORIZATION regress_alter_user1; CREATE TABLE alt_regress_1 (a INTEGER, b INTEGER); ! CREATE STATISTICS alt_stat1 ON (a, b) FROM alt_regress_1; ! CREATE STATISTICS alt_stat2 ON (a, b) FROM alt_regress_1; ALTER STATISTICS alt_stat1 RENAME TO alt_stat2; -- failed (name conflict) ERROR: statistics "alt_stat2" already exists in schema "alt_nsp1" ALTER STATISTICS alt_stat1 RENAME TO alt_stat3; -- failed (name conflict) --- 501,508 ---- -- SET SESSION AUTHORIZATION regress_alter_user1; CREATE TABLE alt_regress_1 (a INTEGER, b INTEGER); ! CREATE STATISTICS alt_stat1 FOR (a, b) FROM alt_regress_1; ! CREATE STATISTICS alt_stat2 FOR (a, b) FROM alt_regress_1; ALTER STATISTICS alt_stat1 RENAME TO alt_stat2; -- failed (name conflict) ERROR: statistics "alt_stat2" already exists in schema "alt_nsp1" ALTER STATISTICS alt_stat1 RENAME TO alt_stat3; -- failed (name conflict) *************** *** 511,518 **** ERROR: must be member of role "regress_alter_user2" ALTER STATISTICS alt_stat2 OWNER TO regress_alter_user3; -- OK ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_user2; ! CREATE STATISTICS alt_stat1 ON (a, b) FROM alt_regress_1; ! CREATE STATISTICS alt_stat2 ON (a, b) FROM alt_regress_1; ALTER STATISTICS alt_stat3 RENAME TO alt_stat4; -- failed (not owner) ERROR: must be owner of statistics alt_stat3 ALTER STATISTICS alt_stat1 RENAME TO alt_stat4; -- OK --- 511,518 ---- ALTER STATISTICS alt_stat2 OWNER TO regress_alter_user3; -- OK ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_user2; ! CREATE STATISTICS alt_stat1 FOR (a, b) FROM alt_regress_1; ! CREATE STATISTICS alt_stat2 FOR (a, b) FROM alt_regress_1; ALTER STATISTICS alt_stat3 RENAME TO alt_stat4; -- failed (not owner) ERROR: must be owner of statistics alt_stat3 ALTER STATISTICS alt_stat1 RENAME TO alt_stat4; -- OK *** a/src/test/regress/expected/object_address.out --- b/src/test/regress/expected/object_address.out *************** *** 39,45 **** CREATE TRANSFORM FOR int LANGUAGE SQL ( CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (NOCONNECT, SLOT NAME = NONE); WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables ! CREATE STATISTICS addr_nsp.gentable_stat ON (a,b) FROM addr_nsp.gentable; -- test some error cases SELECT pg_get_object_address('stone', '{}', '{}'); ERROR: unrecognized object type "stone" --- 39,45 ---- CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (NOCONNECT, SLOT NAME = NONE); WARNING: tables were not subscribed, you will have to run ALTER SUBSCRIPTION ... REFRESH PUBLICATION to subscribe the tables ! CREATE STATISTICS addr_nsp.gentable_stat FOR (a,b) FROM addr_nsp.gentable; -- test some error cases SELECT pg_get_object_address('stone', '{}', '{}'); ERROR: unrecognized object type "stone" *** a/src/test/regress/expected/stats_ext.out --- b/src/test/regress/expected/stats_ext.out *************** *** 5,28 **** SET max_parallel_workers = 0; SET max_parallel_workers_per_gather = 0; SET work_mem = '128kB'; -- Ensure stats are dropped sanely CREATE TABLE ab1 (a INTEGER, b INTEGER, c INTEGER); ! CREATE STATISTICS ab1_a_b_stats ON (a, b) FROM ab1; DROP STATISTICS ab1_a_b_stats; CREATE SCHEMA regress_schema_2; ! CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON (a, b) FROM ab1; -- Let's also verify the pg_get_statisticsextdef output looks sane. SELECT pg_get_statisticsextdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats'; ! pg_get_statisticsextdef ! --------------------------------------------------------------------- ! CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON (a, b) FROM ab1 (1 row) DROP STATISTICS regress_schema_2.ab1_a_b_stats; -- Ensure statistics are dropped when columns are ! CREATE STATISTICS ab1_b_c_stats ON (b, c) FROM ab1; ! CREATE STATISTICS ab1_a_b_c_stats ON (a, b, c) FROM ab1; ! CREATE STATISTICS ab1_a_b_stats ON (a, b) FROM ab1; ALTER TABLE ab1 DROP COLUMN a; \d ab1 Table "public.ab1" --- 5,53 ---- SET max_parallel_workers = 0; SET max_parallel_workers_per_gather = 0; SET work_mem = '128kB'; + -- Verify failures + CREATE STATISTICS tst; + ERROR: syntax error at or near ";" + LINE 1: CREATE STATISTICS tst; + ^ + CREATE STATISTICS tst FOR (a, b); + ERROR: missing FROM clause + CREATE STATISTICS tst FOR (a + b); + ERROR: syntax error at or near "+" + LINE 1: CREATE STATISTICS tst FOR (a + b); + ^ + CREATE STATISTICS tst FROM sometab; + ERROR: missing FOR clause + CREATE STATISTICS tst FROM sometab, othertab; + ERROR: statistics across multiple relations are not supported yet + CREATE STATISTICS tst WITH (fillfactor = 80); + ERROR: WITH clause is not yet implemented + CREATE STATISTICS tst WHERE mars > earth; + ERROR: WHERE clause is not yet implemented + CREATE STATISTICS tst FOR (a, b) FROM nonexistant; + ERROR: relation "nonexistant" does not exist + CREATE STATISTICS tst FOR (a, b) FROM pg_class; + ERROR: column "a" referenced in statistics does not exist + CREATE STATISTICS tst (unrecognized) FOR (relname, relnatts) FROM pg_class; + ERROR: unrecognized statistics type "unrecognized" -- Ensure stats are dropped sanely CREATE TABLE ab1 (a INTEGER, b INTEGER, c INTEGER); ! CREATE STATISTICS ab1_a_b_stats FOR (a, b) FROM ab1; DROP STATISTICS ab1_a_b_stats; CREATE SCHEMA regress_schema_2; ! CREATE STATISTICS regress_schema_2.ab1_a_b_stats FOR (a, b) FROM ab1; -- Let's also verify the pg_get_statisticsextdef output looks sane. SELECT pg_get_statisticsextdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats'; ! pg_get_statisticsextdef ! ---------------------------------------------------------------------- ! CREATE STATISTICS regress_schema_2.ab1_a_b_stats FOR (a, b) FROM ab1 (1 row) DROP STATISTICS regress_schema_2.ab1_a_b_stats; -- Ensure statistics are dropped when columns are ! CREATE STATISTICS ab1_b_c_stats FOR (b, c) FROM ab1; ! CREATE STATISTICS ab1_a_b_c_stats FOR (a, b, c) FROM ab1; ! CREATE STATISTICS ab1_a_b_stats FOR (a, b) FROM ab1; ALTER TABLE ab1 DROP COLUMN a; \d ab1 Table "public.ab1" *************** *** 31,44 **** ALTER TABLE ab1 DROP COLUMN a; b | integer | | | c | integer | | | Statistics: ! "public.ab1_b_c_stats" WITH (ndistinct, dependencies) ON (b, c) DROP TABLE ab1; -- Ensure things work sanely with SET STATISTICS 0 CREATE TABLE ab1 (a INTEGER, b INTEGER); ALTER TABLE ab1 ALTER a SET STATISTICS 0; INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a; ! CREATE STATISTICS ab1_a_b_stats ON (a, b) FROM ab1; ANALYZE ab1; WARNING: extended statistics "public.ab1_a_b_stats" could not be collected for relation public.ab1 ALTER TABLE ab1 ALTER a SET STATISTICS -1; --- 56,69 ---- b | integer | | | c | integer | | | Statistics: ! "public.ab1_b_c_stats" (ndistinct, dependencies) FOR (b, c) DROP TABLE ab1; -- Ensure things work sanely with SET STATISTICS 0 CREATE TABLE ab1 (a INTEGER, b INTEGER); ALTER TABLE ab1 ALTER a SET STATISTICS 0; INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a; ! CREATE STATISTICS ab1_a_b_stats FOR (a, b) FROM ab1; ANALYZE ab1; WARNING: extended statistics "public.ab1_a_b_stats" could not be collected for relation public.ab1 ALTER TABLE ab1 ALTER a SET STATISTICS -1; *************** *** 60,83 **** CREATE SERVER extstats_dummy_srv FOREIGN DATA WRAPPER extstats_dummy_fdw; CREATE FOREIGN TABLE tststats.f (a int, b int, c text) SERVER extstats_dummy_srv; CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b); CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10); ! CREATE STATISTICS tststats.s1 ON (a, b) FROM tststats.t; ! CREATE STATISTICS tststats.s2 ON (a, b) FROM tststats.ti; ERROR: relation "ti" is not a table, foreign table, or materialized view ! CREATE STATISTICS tststats.s3 ON (a, b) FROM tststats.s; ERROR: relation "s" is not a table, foreign table, or materialized view ! CREATE STATISTICS tststats.s4 ON (a, b) FROM tststats.v; ERROR: relation "v" is not a table, foreign table, or materialized view ! CREATE STATISTICS tststats.s5 ON (a, b) FROM tststats.mv; ! CREATE STATISTICS tststats.s6 ON (a, b) FROM tststats.ty; ERROR: relation "ty" is not a table, foreign table, or materialized view ! CREATE STATISTICS tststats.s7 ON (a, b) FROM tststats.f; ! CREATE STATISTICS tststats.s8 ON (a, b) FROM tststats.pt; ! CREATE STATISTICS tststats.s9 ON (a, b) FROM tststats.pt1; DO $$ DECLARE relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass; BEGIN ! EXECUTE 'CREATE STATISTICS tststats.s10 ON (a, b) FROM ' || relname; EXCEPTION WHEN wrong_object_type THEN RAISE NOTICE 'stats on toast table not created'; END; --- 85,108 ---- CREATE FOREIGN TABLE tststats.f (a int, b int, c text) SERVER extstats_dummy_srv; CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b); CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10); ! CREATE STATISTICS tststats.s1 FOR (a, b) FROM tststats.t; ! CREATE STATISTICS tststats.s2 FOR (a, b) FROM tststats.ti; ERROR: relation "ti" is not a table, foreign table, or materialized view ! CREATE STATISTICS tststats.s3 FOR (a, b) FROM tststats.s; ERROR: relation "s" is not a table, foreign table, or materialized view ! CREATE STATISTICS tststats.s4 FOR (a, b) FROM tststats.v; ERROR: relation "v" is not a table, foreign table, or materialized view ! CREATE STATISTICS tststats.s5 FOR (a, b) FROM tststats.mv; ! CREATE STATISTICS tststats.s6 FOR (a, b) FROM tststats.ty; ERROR: relation "ty" is not a table, foreign table, or materialized view ! CREATE STATISTICS tststats.s7 FOR (a, b) FROM tststats.f; ! CREATE STATISTICS tststats.s8 FOR (a, b) FROM tststats.pt; ! CREATE STATISTICS tststats.s9 FOR (a, b) FROM tststats.pt1; DO $$ DECLARE relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass; BEGIN ! EXECUTE 'CREATE STATISTICS tststats.s10 FOR (a, b) FROM ' || relname; EXCEPTION WHEN wrong_object_type THEN RAISE NOTICE 'stats on toast table not created'; END; *************** *** 159,177 **** EXPLAIN (COSTS off) (5 rows) -- unknown column ! CREATE STATISTICS s10 ON (unknown_column) FROM ndistinct; ERROR: column "unknown_column" referenced in statistics does not exist -- single column ! CREATE STATISTICS s10 ON (a) FROM ndistinct; ERROR: extended statistics require at least 2 columns -- single column, duplicated ! CREATE STATISTICS s10 ON (a,a) FROM ndistinct; ERROR: duplicate column name in statistics definition -- two columns, one duplicated ! CREATE STATISTICS s10 ON (a, a, b) FROM ndistinct; ERROR: duplicate column name in statistics definition -- correct command ! CREATE STATISTICS s10 ON (a, b, c) FROM ndistinct; ANALYZE ndistinct; SELECT stxkind, stxndistinct FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass; --- 184,202 ---- (5 rows) -- unknown column ! CREATE STATISTICS s10 FOR (unknown_column) FROM ndistinct; ERROR: column "unknown_column" referenced in statistics does not exist -- single column ! CREATE STATISTICS s10 FOR (a) FROM ndistinct; ERROR: extended statistics require at least 2 columns -- single column, duplicated ! CREATE STATISTICS s10 FOR (a,a) FROM ndistinct; ERROR: duplicate column name in statistics definition -- two columns, one duplicated ! CREATE STATISTICS s10 FOR (a, a, b) FROM ndistinct; ERROR: duplicate column name in statistics definition -- correct command ! CREATE STATISTICS s10 FOR (a, b, c) FROM ndistinct; ANALYZE ndistinct; SELECT stxkind, stxndistinct FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass; *************** *** 389,395 **** EXPLAIN (COSTS OFF) (2 rows) -- create statistics ! CREATE STATISTICS func_deps_stat WITH (dependencies) ON (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies; EXPLAIN (COSTS OFF) SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1'; --- 414,420 ---- (2 rows) -- create statistics ! CREATE STATISTICS func_deps_stat (dependencies) FOR (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies; EXPLAIN (COSTS OFF) SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1'; *************** *** 432,438 **** EXPLAIN (COSTS OFF) (2 rows) -- create statistics ! CREATE STATISTICS func_deps_stat WITH (dependencies) ON (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies; EXPLAIN (COSTS OFF) SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1'; --- 457,463 ---- (2 rows) -- create statistics ! CREATE STATISTICS func_deps_stat (dependencies) FOR (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies; EXPLAIN (COSTS OFF) SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1'; *** a/src/test/regress/sql/alter_generic.sql --- b/src/test/regress/sql/alter_generic.sql *************** *** 438,445 **** DROP OPERATOR FAMILY alt_opf18 USING btree; -- SET SESSION AUTHORIZATION regress_alter_user1; CREATE TABLE alt_regress_1 (a INTEGER, b INTEGER); ! CREATE STATISTICS alt_stat1 ON (a, b) FROM alt_regress_1; ! CREATE STATISTICS alt_stat2 ON (a, b) FROM alt_regress_1; ALTER STATISTICS alt_stat1 RENAME TO alt_stat2; -- failed (name conflict) ALTER STATISTICS alt_stat1 RENAME TO alt_stat3; -- failed (name conflict) --- 438,445 ---- -- SET SESSION AUTHORIZATION regress_alter_user1; CREATE TABLE alt_regress_1 (a INTEGER, b INTEGER); ! CREATE STATISTICS alt_stat1 FOR (a, b) FROM alt_regress_1; ! CREATE STATISTICS alt_stat2 FOR (a, b) FROM alt_regress_1; ALTER STATISTICS alt_stat1 RENAME TO alt_stat2; -- failed (name conflict) ALTER STATISTICS alt_stat1 RENAME TO alt_stat3; -- failed (name conflict) *************** *** 448,455 **** ALTER STATISTICS alt_stat2 OWNER TO regress_alter_user3; -- OK ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_user2; ! CREATE STATISTICS alt_stat1 ON (a, b) FROM alt_regress_1; ! CREATE STATISTICS alt_stat2 ON (a, b) FROM alt_regress_1; ALTER STATISTICS alt_stat3 RENAME TO alt_stat4; -- failed (not owner) ALTER STATISTICS alt_stat1 RENAME TO alt_stat4; -- OK --- 448,455 ---- ALTER STATISTICS alt_stat2 SET SCHEMA alt_nsp2; -- OK SET SESSION AUTHORIZATION regress_alter_user2; ! CREATE STATISTICS alt_stat1 FOR (a, b) FROM alt_regress_1; ! CREATE STATISTICS alt_stat2 FOR (a, b) FROM alt_regress_1; ALTER STATISTICS alt_stat3 RENAME TO alt_stat4; -- failed (not owner) ALTER STATISTICS alt_stat1 RENAME TO alt_stat4; -- OK *** a/src/test/regress/sql/object_address.sql --- b/src/test/regress/sql/object_address.sql *************** *** 41,47 **** CREATE TRANSFORM FOR int LANGUAGE SQL ( TO SQL WITH FUNCTION int4recv(internal)); CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (NOCONNECT, SLOT NAME = NONE); ! CREATE STATISTICS addr_nsp.gentable_stat ON (a,b) FROM addr_nsp.gentable; -- test some error cases SELECT pg_get_object_address('stone', '{}', '{}'); --- 41,47 ---- TO SQL WITH FUNCTION int4recv(internal)); CREATE PUBLICATION addr_pub FOR TABLE addr_nsp.gentable; CREATE SUBSCRIPTION addr_sub CONNECTION '' PUBLICATION bar WITH (NOCONNECT, SLOT NAME = NONE); ! CREATE STATISTICS addr_nsp.gentable_stat FOR (a,b) FROM addr_nsp.gentable; -- test some error cases SELECT pg_get_object_address('stone', '{}', '{}'); *** a/src/test/regress/sql/stats_ext.sql --- b/src/test/regress/sql/stats_ext.sql *************** *** 7,19 **** SET max_parallel_workers = 0; SET max_parallel_workers_per_gather = 0; SET work_mem = '128kB'; -- Ensure stats are dropped sanely CREATE TABLE ab1 (a INTEGER, b INTEGER, c INTEGER); ! CREATE STATISTICS ab1_a_b_stats ON (a, b) FROM ab1; DROP STATISTICS ab1_a_b_stats; CREATE SCHEMA regress_schema_2; ! CREATE STATISTICS regress_schema_2.ab1_a_b_stats ON (a, b) FROM ab1; -- Let's also verify the pg_get_statisticsextdef output looks sane. SELECT pg_get_statisticsextdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats'; --- 7,31 ---- SET max_parallel_workers_per_gather = 0; SET work_mem = '128kB'; + -- Verify failures + CREATE STATISTICS tst; + CREATE STATISTICS tst FOR (a, b); + CREATE STATISTICS tst FOR (a + b); + CREATE STATISTICS tst FROM sometab; + CREATE STATISTICS tst FROM sometab, othertab; + CREATE STATISTICS tst WITH (fillfactor = 80); + CREATE STATISTICS tst WHERE mars > earth; + CREATE STATISTICS tst FOR (a, b) FROM nonexistant; + CREATE STATISTICS tst FOR (a, b) FROM pg_class; + CREATE STATISTICS tst (unrecognized) FOR (relname, relnatts) FROM pg_class; + -- Ensure stats are dropped sanely CREATE TABLE ab1 (a INTEGER, b INTEGER, c INTEGER); ! CREATE STATISTICS ab1_a_b_stats FOR (a, b) FROM ab1; DROP STATISTICS ab1_a_b_stats; CREATE SCHEMA regress_schema_2; ! CREATE STATISTICS regress_schema_2.ab1_a_b_stats FOR (a, b) FROM ab1; -- Let's also verify the pg_get_statisticsextdef output looks sane. SELECT pg_get_statisticsextdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a_b_stats'; *************** *** 21,29 **** SELECT pg_get_statisticsextdef(oid) FROM pg_statistic_ext WHERE stxname = 'ab1_a DROP STATISTICS regress_schema_2.ab1_a_b_stats; -- Ensure statistics are dropped when columns are ! CREATE STATISTICS ab1_b_c_stats ON (b, c) FROM ab1; ! CREATE STATISTICS ab1_a_b_c_stats ON (a, b, c) FROM ab1; ! CREATE STATISTICS ab1_a_b_stats ON (a, b) FROM ab1; ALTER TABLE ab1 DROP COLUMN a; \d ab1 DROP TABLE ab1; --- 33,41 ---- DROP STATISTICS regress_schema_2.ab1_a_b_stats; -- Ensure statistics are dropped when columns are ! CREATE STATISTICS ab1_b_c_stats FOR (b, c) FROM ab1; ! CREATE STATISTICS ab1_a_b_c_stats FOR (a, b, c) FROM ab1; ! CREATE STATISTICS ab1_a_b_stats FOR (a, b) FROM ab1; ALTER TABLE ab1 DROP COLUMN a; \d ab1 DROP TABLE ab1; *************** *** 32,38 **** DROP TABLE ab1; CREATE TABLE ab1 (a INTEGER, b INTEGER); ALTER TABLE ab1 ALTER a SET STATISTICS 0; INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a; ! CREATE STATISTICS ab1_a_b_stats ON (a, b) FROM ab1; ANALYZE ab1; ALTER TABLE ab1 ALTER a SET STATISTICS -1; -- partial analyze doesn't build stats either --- 44,50 ---- CREATE TABLE ab1 (a INTEGER, b INTEGER); ALTER TABLE ab1 ALTER a SET STATISTICS 0; INSERT INTO ab1 SELECT a, a%23 FROM generate_series(1, 1000) a; ! CREATE STATISTICS ab1_a_b_stats FOR (a, b) FROM ab1; ANALYZE ab1; ALTER TABLE ab1 ALTER a SET STATISTICS -1; -- partial analyze doesn't build stats either *************** *** 55,74 **** CREATE FOREIGN TABLE tststats.f (a int, b int, c text) SERVER extstats_dummy_srv CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b); CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10); ! CREATE STATISTICS tststats.s1 ON (a, b) FROM tststats.t; ! CREATE STATISTICS tststats.s2 ON (a, b) FROM tststats.ti; ! CREATE STATISTICS tststats.s3 ON (a, b) FROM tststats.s; ! CREATE STATISTICS tststats.s4 ON (a, b) FROM tststats.v; ! CREATE STATISTICS tststats.s5 ON (a, b) FROM tststats.mv; ! CREATE STATISTICS tststats.s6 ON (a, b) FROM tststats.ty; ! CREATE STATISTICS tststats.s7 ON (a, b) FROM tststats.f; ! CREATE STATISTICS tststats.s8 ON (a, b) FROM tststats.pt; ! CREATE STATISTICS tststats.s9 ON (a, b) FROM tststats.pt1; DO $$ DECLARE relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass; BEGIN ! EXECUTE 'CREATE STATISTICS tststats.s10 ON (a, b) FROM ' || relname; EXCEPTION WHEN wrong_object_type THEN RAISE NOTICE 'stats on toast table not created'; END; --- 67,86 ---- CREATE TABLE tststats.pt (a int, b int, c text) PARTITION BY RANGE (a, b); CREATE TABLE tststats.pt1 PARTITION OF tststats.pt FOR VALUES FROM (-10, -10) TO (10, 10); ! CREATE STATISTICS tststats.s1 FOR (a, b) FROM tststats.t; ! CREATE STATISTICS tststats.s2 FOR (a, b) FROM tststats.ti; ! CREATE STATISTICS tststats.s3 FOR (a, b) FROM tststats.s; ! CREATE STATISTICS tststats.s4 FOR (a, b) FROM tststats.v; ! CREATE STATISTICS tststats.s5 FOR (a, b) FROM tststats.mv; ! CREATE STATISTICS tststats.s6 FOR (a, b) FROM tststats.ty; ! CREATE STATISTICS tststats.s7 FOR (a, b) FROM tststats.f; ! CREATE STATISTICS tststats.s8 FOR (a, b) FROM tststats.pt; ! CREATE STATISTICS tststats.s9 FOR (a, b) FROM tststats.pt1; DO $$ DECLARE relname text := reltoastrelid::regclass FROM pg_class WHERE oid = 'tststats.t'::regclass; BEGIN ! EXECUTE 'CREATE STATISTICS tststats.s10 FOR (a, b) FROM ' || relname; EXCEPTION WHEN wrong_object_type THEN RAISE NOTICE 'stats on toast table not created'; END; *************** *** 114,132 **** EXPLAIN (COSTS off) SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d; -- unknown column ! CREATE STATISTICS s10 ON (unknown_column) FROM ndistinct; -- single column ! CREATE STATISTICS s10 ON (a) FROM ndistinct; -- single column, duplicated ! CREATE STATISTICS s10 ON (a,a) FROM ndistinct; -- two columns, one duplicated ! CREATE STATISTICS s10 ON (a, a, b) FROM ndistinct; -- correct command ! CREATE STATISTICS s10 ON (a, b, c) FROM ndistinct; ANALYZE ndistinct; --- 126,144 ---- SELECT COUNT(*) FROM ndistinct GROUP BY b, c, d; -- unknown column ! CREATE STATISTICS s10 FOR (unknown_column) FROM ndistinct; -- single column ! CREATE STATISTICS s10 FOR (a) FROM ndistinct; -- single column, duplicated ! CREATE STATISTICS s10 FOR (a,a) FROM ndistinct; -- two columns, one duplicated ! CREATE STATISTICS s10 FOR (a, a, b) FROM ndistinct; -- correct command ! CREATE STATISTICS s10 FOR (a, b, c) FROM ndistinct; ANALYZE ndistinct; *************** *** 233,239 **** EXPLAIN (COSTS OFF) SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1' AND c = 1; -- create statistics ! CREATE STATISTICS func_deps_stat WITH (dependencies) ON (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies; --- 245,251 ---- SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1' AND c = 1; -- create statistics ! CREATE STATISTICS func_deps_stat (dependencies) FOR (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies; *************** *** 259,265 **** EXPLAIN (COSTS OFF) SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1' AND c = 1; -- create statistics ! CREATE STATISTICS func_deps_stat WITH (dependencies) ON (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies; --- 271,277 ---- SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1' AND c = 1; -- create statistics ! CREATE STATISTICS func_deps_stat (dependencies) FOR (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies;