diff --git a/doc/src/sgml/perform.sgml b/doc/src/sgml/perform.sgml
index b10b734b90..32e17ee5f8 100644
--- a/doc/src/sgml/perform.sgml
+++ b/doc/src/sgml/perform.sgml
@@ -1132,8 +1132,8 @@ 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;
+CREATE STATISTICS stts (dependencies)
+               ON zip, city FROM zipcodes;
 ANALYZE zipcodes;
 SELECT stxname, stxkeys, stxdependencies
   FROM pg_statistic_ext
@@ -1219,8 +1219,8 @@ 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;
+CREATE STATISTICS stts2 (ndistinct)
+               ON zip, state, city FROM zipcodes;
 ANALYZE zipcodes;
 SELECT stxkeys AS k, stxndistinct AS nd
   FROM pg_statistic_ext
diff --git a/doc/src/sgml/planstats.sgml b/doc/src/sgml/planstats.sgml
index 11580bfd22..ef847b9633 100644
--- a/doc/src/sgml/planstats.sgml
+++ b/doc/src/sgml/planstats.sgml
@@ -526,7 +526,7 @@ 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;
+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,7 +569,7 @@ 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;
+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                                        
diff --git a/doc/src/sgml/ref/create_statistics.sgml b/doc/src/sgml/ref/create_statistics.sgml
index edbcf5840b..84ff52df04 100644
--- a/doc/src/sgml/ref/create_statistics.sgml
+++ b/doc/src/sgml/ref/create_statistics.sgml
@@ -22,8 +22,8 @@ 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> [, ...])
+    [ ( <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,6 +75,19 @@ CREATE STATISTICS [ IF NOT EXISTS ] <replaceable class="PARAMETER">statistics_na
    </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,42 +107,6 @@ 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>
@@ -158,7 +135,7 @@ CREATE TABLE t1 (
 INSERT INTO t1 SELECT i/100, i/500
                  FROM generate_series(1,1000000) s(i);
 
-CREATE STATISTICS s1 WITH (dependencies) ON (a, b) FROM t1;
+CREATE STATISTICS s1 (dependencies) ON a, b FROM t1;
 
 ANALYZE t1;
 
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index 0b9c33e30a..220a3e92a2 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -63,7 +63,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	Relation	rel;
 	Oid			relid;
 	ObjectAddress parentobject,
-				childobject;
+				  childobject;
 	Datum		types[2];		/* one for each possible type of statistics */
 	int			ntypes;
 	ArrayType  *stxkind;
@@ -71,7 +71,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 	bool		build_dependencies;
 	bool		requested_type = false;
 	int			i;
-	ListCell   *l;
+	ListCell   *cell;
 
 	Assert(IsA(stmt, CreateStatsStmt));
 
@@ -100,42 +100,78 @@ CreateStatistics(CreateStatsStmt *stmt)
 				 errmsg("statistics \"%s\" already exist", namestr)));
 	}
 
-	/*
-	 * CREATE STATISTICS will influence future execution plans but does not
-	 * interfere with currently executing plans.  So it should be enough to
-	 * take only ShareUpdateExclusiveLock on relation, conflicting with
-	 * ANALYZE and other DDL that sets statistical information, but not with
-	 * normal queries.
-	 */
-	rel = relation_openrv(stmt->relation, ShareUpdateExclusiveLock);
-	relid = RelationGetRelid(rel);
+	relid = InvalidOid;
 
-	if (rel->rd_rel->relkind != RELKIND_RELATION &&
-		rel->rd_rel->relkind != RELKIND_MATVIEW &&
-		rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
-		rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+	if (list_length(stmt->relations) > 1)
 		ereport(ERROR,
-				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
-				 errmsg("relation \"%s\" is not a table, foreign table, or materialized view",
-						RelationGetRelationName(rel))));
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cross-table statistics not supported yet")));
+
+	foreach(cell, stmt->relations)
+	{
+		Node	   *rln = lfirst(cell);
+
+		if (!IsA(rln, RangeVar))
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("only a single relation is allowed in CREATE STATISTICS")));
+
+		/*
+		 * CREATE STATISTICS will influence future execution plans but does not
+		 * interfere with currently executing plans.  So it should be enough to
+		 * take only ShareUpdateExclusiveLock on relation, conflicting with
+		 * ANALYZE and other DDL that sets statistical information, but not with
+		 * normal queries.
+		 */
+		rel = relation_openrv(castNode(RangeVar, rln), ShareUpdateExclusiveLock);
+		relid = RelationGetRelid(rel);
+
+		if (rel->rd_rel->relkind != RELKIND_RELATION &&
+			rel->rd_rel->relkind != RELKIND_MATVIEW &&
+			rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
+			rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+			ereport(ERROR,
+					(errcode(ERRCODE_WRONG_OBJECT_TYPE),
+					 errmsg("relation \"%s\" is not a table, foreign table, or materialized view",
+							RelationGetRelationName(rel))));
+	}
+	if (relid == InvalidOid)
+		elog(ERROR, "could not find OID for relation in statistic object");
 
 	/*
 	 * Transform column names to array of attnums. While at it, enforce some
 	 * constraints.
 	 */
-	foreach(l, stmt->keys)
+	foreach(cell, stmt->exprs)
 	{
-		char	   *attname = strVal(lfirst(l));
+		Node	   *expr = (Node *) lfirst(cell);
+		ColumnRef  *cref;
+		char	   *attname;
 		HeapTuple	atttuple;
 		Form_pg_attribute attForm;
 		TypeCacheEntry *type;
 
+		if (!IsA(expr, ColumnRef))
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("only simple column references are allowed in %s",
+							"CREATE STATISTICS")));
+
+		cref = castNode(ColumnRef, expr);
+		if (list_length(cref->fields) > 1)
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("only simple column references are allowed in %s",
+							"CREATE STATISTICS")));
+
+		attname = strVal((Value *) linitial(cref->fields));
+
 		atttuple = SearchSysCacheAttName(relid, attname);
 		if (!HeapTupleIsValid(atttuple))
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_COLUMN),
-			  errmsg("column \"%s\" referenced in statistics does not exist",
-					 attname)));
+					 errmsg("column \"%s\" referenced in statistics does not exist",
+							attname)));
 		attForm = (Form_pg_attribute) GETSTRUCT(atttuple);
 
 		/* Disallow use of system attributes in extended stats */
@@ -188,7 +224,7 @@ CreateStatistics(CreateStatsStmt *stmt)
 		if (attnums[i] == attnums[i - 1])
 			ereport(ERROR,
 					(errcode(ERRCODE_DUPLICATE_COLUMN),
-				  errmsg("duplicate column name in statistics definition")));
+					 errmsg("duplicate column name in statistics definition")));
 
 	/* Form an int2vector representation of the sorted column list */
 	stxkeys = buildint2vector(attnums, numcols);
@@ -199,25 +235,25 @@ CreateStatistics(CreateStatsStmt *stmt)
 	 */
 	build_ndistinct = false;
 	build_dependencies = false;
-	foreach(l, stmt->options)
+	foreach(cell, stmt->stat_types)
 	{
-		DefElem    *opt = (DefElem *) lfirst(l);
+		char    *type = strVal((Value *) lfirst(cell));
 
-		if (strcmp(opt->defname, "ndistinct") == 0)
+		if (strcmp(type, "ndistinct") == 0)
 		{
-			build_ndistinct = defGetBoolean(opt);
+			build_ndistinct = true;
 			requested_type = true;
 		}
-		else if (strcmp(opt->defname, "dependencies") == 0)
+		else if (strcmp(type, "dependencies") == 0)
 		{
-			build_dependencies = defGetBoolean(opt);
+			build_dependencies = true;
 			requested_type = true;
 		}
 		else
 			ereport(ERROR,
 					(errcode(ERRCODE_SYNTAX_ERROR),
-					 errmsg("unrecognized STATISTICS option \"%s\"",
-							opt->defname)));
+					 errmsg("unrecognized statistics type \"%s\"",
+							type)));
 	}
 	/* If no statistic type was specified, build them all. */
 	if (!requested_type)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 2d2a9d00b7..edad16cdb9 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3389,14 +3389,25 @@ _copyCreateStatsStmt(const CreateStatsStmt *from)
 	CreateStatsStmt *newnode = makeNode(CreateStatsStmt);
 
 	COPY_NODE_FIELD(defnames);
-	COPY_NODE_FIELD(relation);
-	COPY_NODE_FIELD(keys);
+	COPY_NODE_FIELD(relations);
+	COPY_NODE_FIELD(exprs);
 	COPY_NODE_FIELD(options);
 	COPY_SCALAR_FIELD(if_not_exists);
 
 	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,6 +5132,9 @@ copyObjectImpl(const void *from)
 		case T_CreateStatsStmt:
 			retval = _copyCreateStatsStmt(from);
 			break;
+		case T_CreateStatsArgument:
+			retval = _copyCreateStatsArgument(from);
+			break;
 		case T_CreateFunctionStmt:
 			retval = _copyCreateFunctionStmt(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index b5459cd726..abfe982d1e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1349,8 +1349,8 @@ static bool
 _equalCreateStatsStmt(const CreateStatsStmt *a, const CreateStatsStmt *b)
 {
 	COMPARE_NODE_FIELD(defnames);
-	COMPARE_NODE_FIELD(relation);
-	COMPARE_NODE_FIELD(keys);
+	COMPARE_NODE_FIELD(relations);
+	COMPARE_NODE_FIELD(exprs);
 	COMPARE_NODE_FIELD(options);
 	COMPARE_SCALAR_FIELD(if_not_exists);
 
@@ -1358,6 +1358,15 @@ _equalCreateStatsStmt(const CreateStatsStmt *a, const CreateStatsStmt *b)
 }
 
 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,6 +3279,9 @@ equal(const void *a, const void *b)
 		case T_CreateStatsStmt:
 			retval = _equalCreateStatsStmt(a, b);
 			break;
+		case T_CreateStatsArgument:
+			retval = _equalCreateStatsArgument(a, b);
+			break;
 		case T_CreateFunctionStmt:
 			retval = _equalCreateFunctionStmt(a, b);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 98f67681a7..2334abfb19 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2639,13 +2639,22 @@ _outCreateStatsStmt(StringInfo str, const CreateStatsStmt *node)
 	WRITE_NODE_TYPE("CREATESTATSSTMT");
 
 	WRITE_NODE_FIELD(defnames);
-	WRITE_NODE_FIELD(relation);
-	WRITE_NODE_FIELD(keys);
+	WRITE_NODE_FIELD(relations);
+	WRITE_NODE_FIELD(exprs);
 	WRITE_NODE_FIELD(options);
 	WRITE_BOOL_FIELD(if_not_exists);
 }
 
 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,6 +4060,9 @@ outNode(StringInfo str, const void *obj)
 			case T_CreateStatsStmt:
 				_outCreateStatsStmt(str, obj);
 				break;
+			case T_CreateStatsArgument:
+				_outCreateStatsArgument(str, obj);
+				break;
 			case T_NotifyStmt:
 				_outNotifyStmt(str, obj);
 				break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 65c004c509..f59c38d29d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -3836,31 +3836,24 @@ ExistingIndex:   USING INDEX index_name				{ $$ = $3; }
 /*****************************************************************************
  *
  *		QUERY :
- *				CREATE STATISTICS stats_name WITH (options) ON (columns) FROM relname
+ *				CREATE STATISTICS stats_name [(stat types)]
+ *					ON expression-list FROM joined tables
  *
  *****************************************************************************/
 
+CreateStatsStmt:
+			CREATE opt_if_not_exists STATISTICS any_name
+			opt_name_list ON expr_list FROM from_list
+				{
+					CreateStatsStmt *n = makeNode(CreateStatsStmt);
+					n->defnames = $4;
+					n->stat_types = $5;
+					n->if_not_exists = $2;
 
-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;
-						}
+					n->relations = $9;
+					n->exprs = $7;
+					$$ = (Node *)n;
+				}
 			;
 
 /*****************************************************************************
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index cbde1fff01..983b9800cc 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -1504,15 +1504,15 @@ 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
+	 * 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, " WITH (");
+		appendStringInfoString(&buf, " (");
 		if (ndistinct_enabled)
 			appendStringInfoString(&buf, "ndistinct");
 		else if (dependencies_enabled)
@@ -1521,7 +1521,7 @@ pg_get_statisticsext_worker(Oid statextid, bool missing_ok)
 		appendStringInfoChar(&buf, ')');
 	}
 
-	appendStringInfoString(&buf, " ON (");
+	appendStringInfoString(&buf, " ON ");
 
 	for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
 	{
@@ -1536,7 +1536,7 @@ pg_get_statisticsext_worker(Oid statextid, bool missing_ok)
 		appendStringInfoString(&buf, quote_identifier(attname));
 	}
 
-	appendStringInfo(&buf, ") FROM %s",
+	appendStringInfo(&buf, " FROM %s",
 					 generate_relation_name(statextrec->stxrelid, NIL));
 
 	ReleaseSysCache(statexttup);
diff --git a/src/bin/pg_dump/t/002_pg_dump.pl b/src/bin/pg_dump/t/002_pg_dump.pl
index ce0c9ef54d..f88832e7cb 100644
--- a/src/bin/pg_dump/t/002_pg_dump.pl
+++ b/src/bin/pg_dump/t/002_pg_dump.pl
@@ -4957,9 +4957,9 @@ qr/CREATE TRANSFORM FOR integer LANGUAGE sql \(FROM SQL WITH FUNCTION pg_catalog
 		catch_all    => 'CREATE ... commands',
 		create_order => 97,
 		create_sql   => 'CREATE STATISTICS dump_test.test_ext_stats_no_options
-							ON (col1, col2) FROM dump_test.test_fifth_table',
+							ON col1, col2 FROM dump_test.test_fifth_table',
 		regexp => qr/^
-			\QCREATE STATISTICS dump_test.test_ext_stats_no_options ON (col1, col2) FROM test_fifth_table;\E
+			\QCREATE STATISTICS dump_test.test_ext_stats_no_options ON col1, col2 FROM test_fifth_table;\E
 		    /xms,
 		like => {
 			binary_upgrade          => 1,
@@ -4990,10 +4990,10 @@ 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',
+		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_using WITH (ndistinct) ON (col1, col2) FROM test_fifth_table;\E
+			\QCREATE STATISTICS dump_test.test_ext_stats_opts (ndistinct) ON col1, col2 FROM test_fifth_table;\E
 		    /xms,
 		like => {
 			binary_upgrade          => 1,
diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c
index 13395f5ca6..386af6168f 100644
--- a/src/bin/psql/describe.c
+++ b/src/bin/psql/describe.c
@@ -2355,8 +2355,9 @@ describeOneTableDetails(const char *schemaname,
 		{
 			printfPQExpBuffer(&buf,
 							  "SELECT oid, "
+							  "stxrelid::pg_catalog.regclass, "
 							  "stxnamespace::pg_catalog.regnamespace AS nsp, "
-							  "stxname, stxkeys,\n"
+							  "stxname,\n"
 							  "  (SELECT pg_catalog.string_agg(pg_catalog.quote_ident(attname),', ')\n"
 							  "   FROM pg_catalog.unnest(stxkeys) s(attnum)\n"
 							  "   JOIN pg_catalog.pg_attribute a ON (stxrelid = a.attrelid AND\n"
@@ -2385,9 +2386,9 @@ describeOneTableDetails(const char *schemaname,
 					printfPQExpBuffer(&buf, "    ");
 
 					/* statistics name (qualified with namespace) */
-					appendPQExpBuffer(&buf, "\"%s.%s\" WITH (",
-									  PQgetvalue(result, i, 1),
-									  PQgetvalue(result, i, 2));
+					appendPQExpBuffer(&buf, "\"%s\".\"%s\" (",
+									  PQgetvalue(result, i, 2),
+									  PQgetvalue(result, i, 3));
 
 					/* options */
 					if (strcmp(PQgetvalue(result, i, 5), "t") == 0)
@@ -2401,8 +2402,9 @@ describeOneTableDetails(const char *schemaname,
 						appendPQExpBuffer(&buf, "%sdependencies", gotone ? ", " : "");
 					}
 
-					appendPQExpBuffer(&buf, ") ON (%s)",
-									  PQgetvalue(result, i, 4));
+					appendPQExpBuffer(&buf, ") ON %s FROM %s",
+									  PQgetvalue(result, i, 4),
+									  PQgetvalue(result, i, 1));
 
 					printTableAddFooter(&cont, buf.data);
 				}
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 3bd527700e..7853c259a5 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -453,6 +453,21 @@ static const SchemaQuery Query_for_list_of_foreign_tables = {
 	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,6 +1038,7 @@ static const pgsql_thing_t words_after_create[] = {
 	{"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},
@@ -1783,6 +1799,10 @@ psql_completion(const char *text, int start, int end)
 	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,7 +2139,8 @@ 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",
+			"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,6 +2404,19 @@ psql_completion(const char *text, int start, int end)
 	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_LIST2("(", "ON");
+	else if (Matches4("CREATE", "STATISTICS", MatchAny, "("))
+		COMPLETE_WITH_LIST2("ndistinct", "dependencies");
+	else if (HeadMatches3("CREATE", "STATISTICS", MatchAny) &&
+			 previous_words[0][0] == '(' &&
+			 previous_words[0][strlen(previous_words[0]) - 1] == ')')
+		COMPLETE_WITH_CONST("ON");
+	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,7 +2623,7 @@ 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",
+					  "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) &&
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index f59d719923..2c4e86b9a3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -462,6 +462,7 @@ typedef enum NodeTag
 	T_InferClause,
 	T_OnConflictClause,
 	T_CommonTableExpr,
+	T_CreateStatsArgument,
 	T_RoleSpec,
 	T_TriggerTransition,
 	T_PartitionElem,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 46c23c2530..a1c8aace58 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -2689,12 +2689,26 @@ typedef struct CreateStatsStmt
 {
 	NodeTag		type;
 	List	   *defnames;		/* qualified name (list of Value strings) */
-	RangeVar   *relation;		/* relation to build statistics on */
-	List	   *keys;			/* String nodes naming referenced columns */
+	List	   *stat_types;		/* stat types (list of Value strings) */
+	List	   *relations;		/* relations to build statistics on */
+	List	   *exprs;			/* Expressions to build statistics on */
 	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
  * ----------------------
diff --git a/src/test/regress/expected/alter_generic.out b/src/test/regress/expected/alter_generic.out
index a81a4edfb2..0a00fcf806 100644
--- a/src/test/regress/expected/alter_generic.out
+++ b/src/test/regress/expected/alter_generic.out
@@ -501,8 +501,8 @@ 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;
+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)
@@ -511,8 +511,8 @@ 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;
+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
diff --git a/src/test/regress/expected/object_address.out b/src/test/regress/expected/object_address.out
index 40eeeed3d2..dce85084f3 100644
--- a/src/test/regress/expected/object_address.out
+++ b/src/test/regress/expected/object_address.out
@@ -39,7 +39,7 @@ 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;
+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"
diff --git a/src/test/regress/expected/stats_ext.out b/src/test/regress/expected/stats_ext.out
index 92ac84ac67..4ccdf21a01 100644
--- a/src/test/regress/expected/stats_ext.out
+++ b/src/test/regress/expected/stats_ext.out
@@ -5,24 +5,49 @@
 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:  syntax error at or near ";"
+LINE 1: CREATE STATISTICS tst ON a, b;
+                                     ^
+CREATE STATISTICS tst FROM sometab;
+ERROR:  syntax error at or near "FROM"
+LINE 1: CREATE STATISTICS tst FROM sometab;
+                              ^
+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 ON relname, relname, relnatts FROM pg_class;
+ERROR:  duplicate column name in statistics definition
+CREATE STATISTICS tst ON relnatts + relpages FROM pg_class;
+ERROR:  only simple column references are allowed in CREATE STATISTICS
+CREATE STATISTICS tst ON (relpages, reltuples) FROM pg_class;
+ERROR:  only simple column references are allowed in CREATE STATISTICS
+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;
+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;
+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
+                      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;
+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"
@@ -31,14 +56,14 @@ ALTER TABLE ab1 DROP COLUMN a;
  b      | integer |           |          | 
  c      | integer |           |          | 
 Statistics:
-    "public.ab1_b_c_stats" WITH (ndistinct, dependencies) ON (b, c)
+    "public"."ab1_b_c_stats" (ndistinct, dependencies) ON b, c FROM ab1
 
 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;
+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;
@@ -60,24 +85,24 @@ 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;
+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;
+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;
+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;
+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;
+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;
+	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;
@@ -158,20 +183,8 @@ EXPLAIN (COSTS off)
          ->  Seq Scan on ndistinct
 (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;
+CREATE STATISTICS s10 ON a, b, c FROM ndistinct;
 ANALYZE ndistinct;
 SELECT stxkind, stxndistinct
   FROM pg_statistic_ext WHERE stxrelid = 'ndistinct'::regclass;
@@ -352,7 +365,6 @@ EXPLAIN (COSTS off)
    ->  Seq Scan on ndistinct
 (3 rows)
 
-DROP TABLE ndistinct;
 -- functional dependencies tests
 CREATE TABLE functional_dependencies (
     filler1 TEXT,
@@ -389,7 +401,7 @@ EXPLAIN (COSTS OFF)
 (2 rows)
 
 -- create statistics
-CREATE STATISTICS func_deps_stat WITH (dependencies) ON (a, b, c) FROM functional_dependencies;
+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,7 +444,7 @@ EXPLAIN (COSTS OFF)
 (2 rows)
 
 -- create statistics
-CREATE STATISTICS func_deps_stat WITH (dependencies) ON (a, b, c) FROM functional_dependencies;
+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';
@@ -456,4 +468,3 @@ EXPLAIN (COSTS OFF)
 (5 rows)
 
 RESET random_page_cost;
-DROP TABLE functional_dependencies;
diff --git a/src/test/regress/sql/alter_generic.sql b/src/test/regress/sql/alter_generic.sql
index 88e8d7eb86..8d1cb55a9f 100644
--- a/src/test/regress/sql/alter_generic.sql
+++ b/src/test/regress/sql/alter_generic.sql
@@ -438,8 +438,8 @@ 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;
+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)
@@ -448,8 +448,8 @@ 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;
+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
diff --git a/src/test/regress/sql/object_address.sql b/src/test/regress/sql/object_address.sql
index 6940392c01..70f5f1c1af 100644
--- a/src/test/regress/sql/object_address.sql
+++ b/src/test/regress/sql/object_address.sql
@@ -41,7 +41,7 @@ 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;
+CREATE STATISTICS addr_nsp.gentable_stat ON a, b FROM addr_nsp.gentable;
 
 -- test some error cases
 SELECT pg_get_object_address('stone', '{}', '{}');
diff --git a/src/test/regress/sql/stats_ext.sql b/src/test/regress/sql/stats_ext.sql
index 72c7659c4b..4050f33c08 100644
--- a/src/test/regress/sql/stats_ext.sql
+++ b/src/test/regress/sql/stats_ext.sql
@@ -7,13 +7,24 @@ SET max_parallel_workers = 0;
 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 FROM sometab;
+CREATE STATISTICS tst ON a, b FROM nonexistant;
+CREATE STATISTICS tst ON a, b FROM pg_class;
+CREATE STATISTICS tst ON relname, relname, relnatts FROM pg_class;
+CREATE STATISTICS tst ON relnatts + relpages FROM pg_class;
+CREATE STATISTICS tst ON (relpages, reltuples) 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;
+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;
+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';
@@ -21,9 +32,9 @@ 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;
+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;
@@ -32,7 +43,7 @@ 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;
+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
@@ -55,20 +66,20 @@ 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;
+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;
+	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;
@@ -113,20 +124,8 @@ EXPLAIN (COSTS off)
 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;
+CREATE STATISTICS s10 ON a, b, c FROM ndistinct;
 
 ANALYZE ndistinct;
 
@@ -202,8 +201,6 @@ EXPLAIN (COSTS off)
 EXPLAIN (COSTS off)
  SELECT COUNT(*) FROM ndistinct GROUP BY a, d;
 
-DROP TABLE ndistinct;
-
 -- functional dependencies tests
 CREATE TABLE functional_dependencies (
     filler1 TEXT,
@@ -233,7 +230,7 @@ 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;
+CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
 
 ANALYZE functional_dependencies;
 
@@ -259,7 +256,7 @@ 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;
+CREATE STATISTICS func_deps_stat (dependencies) ON a, b, c FROM functional_dependencies;
 
 ANALYZE functional_dependencies;
 
@@ -270,4 +267,3 @@ EXPLAIN (COSTS OFF)
  SELECT * FROM functional_dependencies WHERE a = 1 AND b = '1' AND c = 1;
 
 RESET random_page_cost;
-DROP TABLE functional_dependencies;
