agora inbox for [email protected]
help / color / mirror / Atom feedreferences to packaged versions of PostgreSQL
269+ messages / 4 participants
[nested] [flat]
* references to packaged versions of PostgreSQL
@ 1999-01-13 17:51 Brook Milligan <[email protected]>
0 siblings, 2 replies; 269+ messages in thread
From: Brook Milligan @ 1999-01-13 17:51 UTC (permalink / raw)
To: [email protected]
In light of the recent inquiry about postgresql RPMs, it might make
sense to have on our web page some references to packaged up versions
(or other means of easily installing postgresql) for specific
operating systems. As an effort to start that, here are some relevant
URLs pointing to the NetBSD package system for postgresql (and some
more general ones). Note that even though the package page is
retrievable via ftp it is a browsable page.
NetBSD PostgreSQL package ftp://ftp.netbsd.org/pub/NetBSD/packages/pkgsrc/databases/postgresql/README.html
NetBSD home page http://www.netbsd.org
NetBSD packages information http://www.netbsd.org/Documentation/software/packages.html
Perhaps a page that went something like the following would be useful.
It could be filled in with relevant entries as we become aware of
them, and might point people to relatively easy installation pathways
if they need it.
Cheers,
Brook
===========================================================================
Several packages for PostgreSQL have been created to ease the process
of installing the system under specific operating systems. See the
following documents for more information.
Operating system Package name
---------------- ------------
NetBSD <ref URL> databases/postgresql <ref URL>
...
^ permalink raw reply [nested|flat] 269+ messages in thread
* Re: [HACKERS] references to packaged versions of PostgreSQL
@ 1999-01-13 18:02 Hal Snyder <[email protected]>
parent: Brook Milligan <[email protected]>
1 sibling, 0 replies; 269+ messages in thread
From: Hal Snyder @ 1999-01-13 18:02 UTC (permalink / raw)
To: pgsql-hackers
Brook Milligan <[email protected]> writes:
> In light of the recent inquiry about postgresql RPMs, it might make
> sense to have on our web page some references to packaged up versions
> (or other means of easily installing postgresql) for specific
> operating systems. As an effort to start that, here are some relevant
...
> Perhaps a page that went something like the following would be useful.
...
Looks great! I'll have a go at it tonight.
Objections, comments, anyone?
---
Hal / [email protected] / [email protected]
^ permalink raw reply [nested|flat] 269+ messages in thread
* Re: [HACKERS] references to packaged versions of PostgreSQL
@ 1999-01-13 21:09 Oliver Elphick <[email protected]>
parent: Brook Milligan <[email protected]>
1 sibling, 0 replies; 269+ messages in thread
From: Oliver Elphick @ 1999-01-13 21:09 UTC (permalink / raw)
To: [email protected]
Brook Milligan wrote:
>In light of the recent inquiry about postgresql RPMs, it might make
>sense to have on our web page some references to packaged up versions
>(or other means of easily installing postgresql) for specific
>operating systems.
PostgreSQL for Debian GNU/Linux:
Web page - http://www.debian.org
FTP - ftp://ftp.debian.org and mirrors worldwide
The PostgreSQL source package (postgresql) produces several binary
packages:
Section Package name [comments]
------- ------------ ----------
misc postgresql
libs libpgsql [libpq.so.1]
libs libpgsql2 [libpq.so.2]
libs libpgtcl
libs libpgperl
libs ecpg
libs libpgjava [not available yet for 6.4.2]
devel postgresql-dev
doc postgresql-doc
[the following packages are only in Debian's unstable release]
libs postgresql-pl
libs python-postgresql [does not exist yet]
misc postgresql-contrib
misc postgresql-test
The current Debian stable release (hamm) contains release 6.3.2 of
PostgreSQL, as will the currently frozen next release (slink); the
unstable release (potato) contains 6.4.2.
Releases are found in the dists/ directory in the ftp archive.
--
Oliver Elphick [email protected]
Isle of Wight http://www.lfix.co.uk/oliver
PGP key from public servers; key ID 32B8FAA1
========================================
"Wherefore let him that thinketh he standeth take heed
lest he fall." I Corinthians 10:12
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
* [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt
@ 2025-08-28 17:49 Álvaro Herrera <[email protected]>
0 siblings, 0 replies; 269+ messages in thread
From: Álvaro Herrera @ 2025-08-28 17:49 UTC (permalink / raw)
---
src/backend/commands/statscmds.c | 26 +++++-----------
src/backend/commands/tablecmds.c | 4 +--
src/backend/parser/parse_utilcmd.c | 13 +++++---
src/backend/tcop/utility.c | 48 +++++++++++++++++-------------
src/include/commands/defrem.h | 2 +-
src/include/parser/parse_utilcmd.h | 2 +-
6 files changed, 49 insertions(+), 46 deletions(-)
diff --git a/src/backend/commands/statscmds.c b/src/backend/commands/statscmds.c
index e24d540cd45..07b2b5bfef3 100644
--- a/src/backend/commands/statscmds.c
+++ b/src/backend/commands/statscmds.c
@@ -59,7 +59,7 @@ compare_int16(const void *a, const void *b)
* CREATE STATISTICS
*/
ObjectAddress
-CreateStatistics(CreateStatsStmt *stmt)
+CreateStatistics(CreateStatsStmt *stmt, List *relids)
{
int16 attnums[STATS_MAX_DIMENSIONS];
int nattnums = 0;
@@ -92,28 +92,18 @@ CreateStatistics(CreateStatsStmt *stmt)
ListCell *cell;
ListCell *cell2;
- Assert(IsA(stmt, CreateStatsStmt));
-
/*
* Examine the FROM clause. Currently, we only allow it to be a single
* simple table, but later we'll probably allow multiple tables and JOIN
- * syntax. The grammar is already prepared for that, so we have to check
- * here that what we got is what we can support.
+ * syntax. Parse analysis checked the list length already, so this is
+ * just defense-in-depth.
*/
- if (list_length(stmt->relations) != 1)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ Assert(list_length(stmt->relations) == list_length(relids));
+ if (list_length(relids) != 1)
+ elog(ERROR, "only a single relation is allowed in CREATE STATISTICS");
- foreach(cell, stmt->relations)
+ foreach_oid(relid, relids)
{
- Node *rln = (Node *) 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
@@ -121,7 +111,7 @@ CreateStatistics(CreateStatsStmt *stmt)
* conflicting with ANALYZE and other DDL that sets statistical
* information, but not with normal queries.
*/
- rel = relation_openrv((RangeVar *) rln, ShareUpdateExclusiveLock);
+ rel = relation_open(relid, ShareUpdateExclusiveLock);
/* Restrict to allowed relation types */
if (rel->rd_rel->relkind != RELKIND_RELATION &&
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 082a3575d62..1779bae80cb 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -9654,7 +9654,7 @@ ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
/* The CreateStatsStmt has already been through transformStatsStmt */
Assert(stmt->transformed);
- address = CreateStatistics(stmt);
+ address = CreateStatistics(stmt, list_make1_oid(RelationGetRelid(rel)));
return address;
}
@@ -15631,7 +15631,7 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
else if (IsA(stmt, CreateStatsStmt))
querytree_list = lappend(querytree_list,
- transformStatsStmt(oldRelId,
+ transformStatsStmt(list_make1_oid(oldRelId),
(CreateStatsStmt *) stmt,
cmd));
else
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index afcf54169c3..587e2ef439c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -3138,11 +3138,11 @@ transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
* transformStatsStmt - parse analysis for CREATE STATISTICS
*
* To avoid race conditions, it's important that this function relies only on
- * the passed-in relid (and not on stmt->relation) to determine the target
- * relation.
+ * the passed-in relids list (and not on stmt->relations) to determine the
+ * target relation.
*/
CreateStatsStmt *
-transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
+transformStatsStmt(List *relids, CreateStatsStmt *stmt, const char *queryString)
{
ParseState *pstate;
ParseNamespaceItem *nsitem;
@@ -3153,6 +3153,11 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
if (stmt->transformed)
return stmt;
+ if (list_length(relids) != 1)
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only a single relation is allowed in CREATE STATISTICS"));
+
/* Set up pstate */
pstate = make_parsestate(NULL);
pstate->p_sourcetext = queryString;
@@ -3162,7 +3167,7 @@ transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
* to its fields without qualification. Caller is responsible for locking
* relation, but we still need to open it.
*/
- rel = relation_open(relid, NoLock);
+ rel = relation_open(linitial_oid(relids), NoLock);
nsitem = addRangeTableEntryForRelation(pstate, rel,
AccessShareLock,
NULL, false, true);
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index 4f4191b0ea6..e09da051310 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1867,32 +1867,40 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateStatsStmt:
{
- Oid relid;
CreateStatsStmt *stmt = (CreateStatsStmt *) parsetree;
- RangeVar *rel = (RangeVar *) linitial(stmt->relations);
+ List *relids = NIL;
+ ListCell *cell;
- if (!IsA(rel, RangeVar))
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("only a single relation is allowed in CREATE STATISTICS")));
+ foreach(cell, stmt->relations)
+ {
+ Oid relid;
- /*
- * CREATE STATISTICS will influence future execution plans
- * but does not interfere with currently executing plans.
- * So it should be enough to take ShareUpdateExclusiveLock
- * on relation, conflicting with ANALYZE and other DDL
- * that sets statistical information, but not with normal
- * queries.
- *
- * XXX RangeVarCallbackOwnsRelation not needed here, to
- * keep the same behavior as before.
- */
- relid = RangeVarGetRelid(rel, ShareUpdateExclusiveLock, false);
+ if (!IsA(lfirst(cell), RangeVar))
+ ereport(ERROR,
+ errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot create statistics on specified relation"),
+ errdetail("CREATE STATISTICS only supports tables, materialized views, foreign tables, and partitioned tables."));
+ /*
+ * CREATE STATISTICS will influence future execution
+ * plans but does not interfere with currently
+ * executing plans. So it should be enough to take
+ * ShareUpdateExclusiveLock on relation, conflicting
+ * with ANALYZE and other DDL that sets statistical
+ * information, but not with normal queries.
+ *
+ * XXX RangeVarCallbackOwnsRelation not needed here,
+ * to keep the same behavior as before.
+ */
+ relid = RangeVarGetRelid(castNode(RangeVar, lfirst(cell)),
+ ShareUpdateExclusiveLock, false);
+ relids = lappend_oid(relids, relid);
+ }
/* Run parse analysis ... */
- stmt = transformStatsStmt(relid, stmt, queryString);
+ stmt = transformStatsStmt(relids, stmt, queryString);
- address = CreateStatistics(stmt);
+ /* ... and execute the command */
+ address = CreateStatistics(stmt, relids);
}
break;
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index dd22b5efdfd..7b8cb40cd83 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -85,7 +85,7 @@ extern void RemoveOperatorById(Oid operOid);
extern ObjectAddress AlterOperator(AlterOperatorStmt *stmt);
/* commands/statscmds.c */
-extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt);
+extern ObjectAddress CreateStatistics(CreateStatsStmt *stmt, List *relids);
extern ObjectAddress AlterStatistics(AlterStatsStmt *stmt);
extern void RemoveStatisticsById(Oid statsOid);
extern void RemoveStatisticsDataById(Oid statsOid, bool inh);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 9f2b58de797..28165eb1198 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -26,7 +26,7 @@ extern AlterTableStmt *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
List **afterStmts);
extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt,
const char *queryString);
-extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
+extern CreateStatsStmt *transformStatsStmt(List *relids, CreateStatsStmt *stmt,
const char *queryString);
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
List **actions, Node **whereClause);
--
2.39.5
--pbl4mx6qlbtkw6pf
Content-Type: text/x-diff; charset=utf-8
Content-Disposition: attachment;
filename="0001-CREATE-STATISTICS-Fix-error-message.patch"
^ permalink raw reply [nested|flat] 269+ messages in thread
end of thread, other threads:[~2025-08-28 17:49 UTC | newest]
Thread overview: 269+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
1999-01-13 17:51 references to packaged versions of PostgreSQL Brook Milligan <[email protected]>
1999-01-13 18:02 ` Hal Snyder <[email protected]>
1999-01-13 21:09 ` Oliver Elphick <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
2025-08-28 17:49 [PATCH] rewrite ProcessUtilitySlow code for CreateStatsStmt Álvaro Herrera <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox