Here's a patch implementing this idea. From gram.y's comment, the support syntax is now:
/***************************************************************************** * * QUERY : ! * CREATE STATISTICS stats_name [(stat types)] arguments ! ! * where 'arguments' can be one or more of: ! * { ON (columns) ! * | FROM relations ! * | WITH (options) ! * | WHERE expression } Note that I removed the USING keyword in the stat types list, and also made it mandatory that that list appears immediately after the new stats name. This should make it possible to have USING in the relation list (the FROM clause), if we allow explicit multiple relations with join syntax there. The other options can appear in any order. Also, both WITH and WHERE are accepted by the grammar, but immediately throw "feature not implemented" error at parse time. I was on the fence about adding copy/equal/out support for the new StatisticArgument node; it seems pointless because that node does not leave gram.y anyway. Unless there are objections, I'll push this tomorrow. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
*** a/doc/src/sgml/perform.sgml --- b/doc/src/sgml/perform.sgml *************** *** 1132,1138 **** WHERE tablename = 'road'; To inspect functional dependencies on a statistics <literal>stts</literal>, you may do this: <programlisting> ! 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 <literal>stts</literal>, you may do this: <programlisting> ! 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: <programlisting> ! 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: <programlisting> ! 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: <programlisting> ! 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: <programlisting> ! 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: <programlisting> 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: <programlisting> 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,28 **** PostgreSQL documentation <refsynopsisdiv> <synopsis> CREATE STATISTICS [ IF NOT EXISTS ] <replaceable class="PARAMETER">statistics_name</replaceable> ! WITH ( <replaceable class="PARAMETER">option</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ON ( <replaceable class="PARAMETER">column_name</replaceable>, <replaceable class="PARAMETER">column_name</replaceable> [, ...]) FROM <replaceable class="PARAMETER">table_name</replaceable> </synopsis> --- 22,28 ---- <refsynopsisdiv> <synopsis> CREATE STATISTICS [ IF NOT EXISTS ] <replaceable class="PARAMETER">statistics_name</replaceable> ! [ ( <replaceable class="PARAMETER">statistic_type</replaceable> [, ... ] ) ] ON ( <replaceable class="PARAMETER">column_name</replaceable>, <replaceable class="PARAMETER">column_name</replaceable> [, ...]) FROM <replaceable class="PARAMETER">table_name</replaceable> </synopsis> *************** *** 75,80 **** CREATE STATISTICS [ IF NOT EXISTS ] <replaceable class="PARAMETER">statistics_na --- 75,93 ---- </varlistentry> <varlistentry> + <term><replaceable class="PARAMETER">statistic_type</replaceable></term> + <listitem> + <para> + A statistic type to be enabled for this statistics. Currently + supported types are <literal>ndistinct</literal>, which enables + n-distinct coefficient tracking, + and <literal>dependencies</literal>, which enables functional + dependencies. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><replaceable class="PARAMETER">column_name</replaceable></term> <listitem> <para> *************** *** 94,135 **** CREATE STATISTICS [ IF NOT EXISTS ] <replaceable class="PARAMETER">statistics_na </varlistentry> </variablelist> - - <refsect2 id="SQL-CREATESTATISTICS-parameters"> - <title id="SQL-CREATESTATISTICS-parameters-title">Parameters</title> - - <indexterm zone="sql-createstatistics-parameters"> - <primary>statistics parameters</primary> - </indexterm> - - <para> - The <literal>WITH</> clause can specify <firstterm>options</> - for the statistics. Available options are listed below. - </para> - - <variablelist> - - <varlistentry> - <term><literal>dependencies</> (<type>boolean</>)</term> - <listitem> - <para> - Enables functional dependencies for the statistics. - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><literal>ndistinct</> (<type>boolean</>)</term> - <listitem> - <para> - Enables ndistinct coefficients for the statistics. - </para> - </listitem> - </varlistentry> - - </variablelist> - - </refsect2> </refsect1> <refsect1> --- 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 <cstatarg> StatisticArgument %type <list> group_by_list %type <node> group_by_item empty_grouping_set rollup_clause cube_clause *************** *** 3828,3860 **** 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 : --- 3832,3905 ---- /***************************************************************************** * * QUERY : ! * CREATE STATISTICS stats_name [(stat types)] arguments ! ! * where 'arguments' can be one or more of: ! * { ON (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: + ON '(' 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 : *************** *** 15872,15877 **** processCASbits(int cas_bits, int location, const char *constrType, --- 15917,15969 ---- } } + /* + * 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 ON 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 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, " ("); if (ndistinct_enabled) appendStringInfoString(&buf, "ndistinct"); else if (dependencies_enabled) *** a/src/bin/pg_dump/t/002_pg_dump.pl --- b/src/bin/pg_dump/t/002_pg_dump.pl *************** *** 4958,4967 **** 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, --- 4958,4967 ---- 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)); *** 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/stats_ext.out --- b/src/test/regress/expected/stats_ext.out *************** *** 5,10 **** --- 5,35 ---- 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 ON (a, b); + ERROR: missing FROM clause + CREATE STATISTICS tst ON (a + b); + ERROR: syntax error at or near "+" + LINE 1: CREATE STATISTICS tst ON (a + b); + ^ + CREATE STATISTICS tst FROM sometab; + ERROR: missing ON 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 ON (a, b) FROM nonexistant; + ERROR: relation "nonexistant" does not exist + CREATE STATISTICS tst ON (a, b) FROM pg_class; + ERROR: column "a" referenced in statistics does not exist + CREATE STATISTICS tst (unrecognized) ON (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 ON (a, b) FROM ab1; *************** *** 31,37 **** 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 --- 56,62 ---- b | integer | | | c | integer | | | Statistics: ! "public.ab1_b_c_stats" (ndistinct, dependencies) ON (b, c) DROP TABLE ab1; -- Ensure things work sanely with SET STATISTICS 0 *************** *** 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) ON (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) ON (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/stats_ext.sql --- b/src/test/regress/sql/stats_ext.sql *************** *** 7,12 **** SET max_parallel_workers = 0; --- 7,24 ---- SET max_parallel_workers_per_gather = 0; SET work_mem = '128kB'; + -- Verify failures + CREATE STATISTICS tst; + CREATE STATISTICS tst ON (a, b); + CREATE STATISTICS tst ON (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 ON (a, b) FROM nonexistant; + CREATE STATISTICS tst ON (a, b) FROM pg_class; + CREATE STATISTICS tst (unrecognized) ON (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 ON (a, b) FROM ab1; *************** *** 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) ON (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) ON (a, b, c) FROM functional_dependencies; ANALYZE functional_dependencies;
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers