*** 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,29 **** 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,29 ----
   <refsynopsisdiv>
  <synopsis>
  CREATE STATISTICS [ IF NOT EXISTS ] <replaceable class="PARAMETER">statistics_name</replaceable>
!     [ ( <replaceable class="PARAMETER">statistic_type</replaceable> [, ... ] ) ]
!     FOR ( <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
***************
*** 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 <name> */
+ 	else if (Matches3("ALTER", "STATISTICS", MatchAny))
+ 		COMPLETE_WITH_LIST3("OWNER TO", "RENAME TO", "SET SCHEMA");
+ 
  	/* ALTER TRIGGER <name>, 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 <name> */
+ 	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;
  
