Skip to content

Commit 04eb587

Browse files
tglsfdcCommitfest Bot
authored andcommitted
Don't try to re-order the subcommands of CREATE SCHEMA
transformCreateSchemaStmtElements has always believed that it is supposed to re-order the subcommands of CREATE SCHEMA into a safe execution order. However, it is nowhere near being capable of doing that correctly. Nor is there reason to think that it ever will be, or that that is a well-defined requirement, or that there's any basis in the SQL standard for it. Moreover, the problem will get worse as we add more subcommand types. Let's just drop the whole idea and execute the commands in the order given, which seems like a much less astonishment-prone definition anyway. Along the way, pass down a ParseState so that we can provide an error cursor for the "wrong schema name" error, and fix transformCreateSchemaStmtElements so that it doesn't scribble on the parsetree passed to it. Note: This will cause compatibility issue, for example: CREATE SCHEMA regress_schema_2 CREATE VIEW abcd_view AS SELECT a FROM abcd CREATE TABLE abcd (a int); With the patch, it will throw an error, whereas on HEAD it won’t. Discussion: https://postgr.es/m/[email protected]
1 parent 74b41f5 commit 04eb587

File tree

14 files changed

+132
-115
lines changed

14 files changed

+132
-115
lines changed

doc/src/sgml/ref/create_schema.sgml

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -193,12 +193,10 @@ CREATE VIEW hollywood.winners AS
193193
</para>
194194

195195
<para>
196-
The SQL standard specifies that the subcommands in <command>CREATE
197-
SCHEMA</command> can appear in any order. The present
198-
<productname>PostgreSQL</productname> implementation does not
199-
handle all cases of forward references in subcommands; it might
200-
sometimes be necessary to reorder the subcommands in order to avoid
201-
forward references.
196+
<productname>PostgreSQL</productname> executes the subcommands
197+
in <command>CREATE SCHEMA</command> in the order given. Other
198+
implementations may try to rearrange the subcommands into dependency
199+
order, but that is hard if not impossible to do correctly.
202200
</para>
203201

204202
<para>

src/backend/commands/extension.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,14 +1773,17 @@ CreateExtensionInternal(char *extensionName,
17731773

17741774
if (!OidIsValid(schemaOid))
17751775
{
1776+
ParseState *pstate = make_parsestate(NULL);
17761777
CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
17771778

1779+
pstate->p_sourcetext = "(generated CREATE SCHEMA command)";
1780+
17781781
csstmt->schemaname = schemaName;
17791782
csstmt->authrole = NULL; /* will be created by current user */
17801783
csstmt->schemaElts = NIL;
17811784
csstmt->if_not_exists = false;
1782-
CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
1783-
-1, -1);
1785+
1786+
CreateSchemaCommand(pstate, csstmt, -1, -1);
17841787

17851788
/*
17861789
* CreateSchemaCommand includes CommandCounterIncrement, so new

src/backend/commands/schemacmds.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerI
4949
* a subquery.
5050
*/
5151
Oid
52-
CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
52+
CreateSchemaCommand(ParseState *pstate, CreateSchemaStmt *stmt,
5353
int stmt_location, int stmt_len)
5454
{
5555
const char *schemaName = stmt->schemaname;
@@ -189,12 +189,13 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
189189

190190
/*
191191
* Examine the list of commands embedded in the CREATE SCHEMA command, and
192-
* reorganize them into a sequentially executable order with no forward
193-
* references. Note that the result is still a list of raw parsetrees ---
194-
* we cannot, in general, run parse analysis on one statement until we
195-
* have actually executed the prior ones.
192+
* do preliminary transformations (mostly, verify that none are trying to
193+
* create objects outside the new schema). Note that the result is still
194+
* a list of raw parsetrees --- we cannot, in general, run parse analysis
195+
* on one statement until we have actually executed the prior ones.
196196
*/
197-
parsetree_list = transformCreateSchemaStmtElements(stmt->schemaElts,
197+
parsetree_list = transformCreateSchemaStmtElements(pstate,
198+
stmt->schemaElts,
198199
schemaName);
199200

200201
/*
@@ -219,7 +220,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
219220

220221
/* do this step */
221222
ProcessUtility(wrapper,
222-
queryString,
223+
pstate->p_sourcetext,
223224
false,
224225
PROCESS_UTILITY_SUBCOMMAND,
225226
NULL,

src/backend/parser/parse_utilcmd.c

Lines changed: 54 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,6 @@ typedef struct
9595
bool ofType; /* true if statement contains OF typename */
9696
} CreateStmtContext;
9797

98-
/* State shared by transformCreateSchemaStmtElements and its subroutines */
99-
typedef struct
100-
{
101-
const char *schemaname; /* name of schema */
102-
List *sequences; /* CREATE SEQUENCE items */
103-
List *tables; /* CREATE TABLE items */
104-
List *views; /* CREATE VIEW items */
105-
List *indexes; /* CREATE INDEX items */
106-
List *triggers; /* CREATE TRIGGER items */
107-
List *grants; /* GRANT items */
108-
} CreateSchemaStmtContext;
109-
11098

11199
static void transformColumnDefinition(CreateStmtContext *cxt,
112100
ColumnDef *column);
@@ -133,7 +121,8 @@ static void transformCheckConstraints(CreateStmtContext *cxt,
133121
static void transformConstraintAttrs(CreateStmtContext *cxt,
134122
List *constraintList);
135123
static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
136-
static void setSchemaName(const char *context_schema, char **stmt_schema_name);
124+
static void checkSchemaName(ParseState *pstate, const char *context_schema,
125+
RangeVar *relation);
137126
static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd);
138127
static List *transformPartitionRangeBounds(ParseState *pstate, List *blist,
139128
Relation parent);
@@ -4075,109 +4064,85 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
40754064
* transformCreateSchemaStmtElements -
40764065
* analyzes the elements of a CREATE SCHEMA statement
40774066
*
4078-
* Split the schema element list from a CREATE SCHEMA statement into
4079-
* individual commands and place them in the result list in an order
4080-
* such that there are no forward references (e.g. GRANT to a table
4081-
* created later in the list). Note that the logic we use for determining
4082-
* forward references is presently quite incomplete.
4067+
* This is now somewhat vestigial: its only real responsibility is to complain
4068+
* if any of the elements are trying to create objects outside the new schema.
4069+
* We used to try to re-order the commands in a way that would work even if
4070+
* the user-written order would not, but that's too hard (perhaps impossible)
4071+
* to do correctly with not-yet-parse-analyzed commands. Now we'll just
4072+
* execute the elements in the order given.
40834073
*
40844074
* "schemaName" is the name of the schema that will be used for the creation
4085-
* of the objects listed, that may be compiled from the schema name defined
4075+
* of the objects listed. It may be obtained from the schema name defined
40864076
* in the statement or a role specification.
40874077
*
4088-
* SQL also allows constraints to make forward references, so thumb through
4089-
* the table columns and move forward references to a posterior alter-table
4090-
* command.
4091-
*
40924078
* The result is a list of parse nodes that still need to be analyzed ---
40934079
* but we can't analyze the later commands until we've executed the earlier
40944080
* ones, because of possible inter-object references.
4095-
*
4096-
* Note: this breaks the rules a little bit by modifying schema-name fields
4097-
* within passed-in structs. However, the transformation would be the same
4098-
* if done over, so it should be all right to scribble on the input to this
4099-
* extent.
41004081
*/
41014082
List *
4102-
transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
4083+
transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts,
4084+
const char *schemaName)
41034085
{
4104-
CreateSchemaStmtContext cxt;
4105-
List *result;
4106-
ListCell *elements;
4107-
4108-
cxt.schemaname = schemaName;
4109-
cxt.sequences = NIL;
4110-
cxt.tables = NIL;
4111-
cxt.views = NIL;
4112-
cxt.indexes = NIL;
4113-
cxt.triggers = NIL;
4114-
cxt.grants = NIL;
4086+
List *elements = NIL;
4087+
ListCell *lc;
41154088

41164089
/*
4117-
* Run through each schema element in the schema element list. Separate
4118-
* statements by type, and do preliminary analysis.
4090+
* Run through each schema element in the schema element list. Check
4091+
* target schema names, and collect the list of actions to be done.
41194092
*/
4120-
foreach(elements, schemaElts)
4093+
foreach(lc, schemaElts)
41214094
{
4122-
Node *element = lfirst(elements);
4095+
Node *element = lfirst(lc);
41234096

41244097
switch (nodeTag(element))
41254098
{
41264099
case T_CreateSeqStmt:
41274100
{
41284101
CreateSeqStmt *elp = (CreateSeqStmt *) element;
41294102

4130-
setSchemaName(cxt.schemaname, &elp->sequence->schemaname);
4131-
cxt.sequences = lappend(cxt.sequences, element);
4103+
checkSchemaName(pstate, schemaName, elp->sequence);
4104+
elements = lappend(elements, element);
41324105
}
41334106
break;
41344107

41354108
case T_CreateStmt:
41364109
{
41374110
CreateStmt *elp = (CreateStmt *) element;
41384111

4139-
setSchemaName(cxt.schemaname, &elp->relation->schemaname);
4140-
4141-
/*
4142-
* XXX todo: deal with constraints
4143-
*/
4144-
cxt.tables = lappend(cxt.tables, element);
4112+
checkSchemaName(pstate, schemaName, elp->relation);
4113+
elements = lappend(elements, element);
41454114
}
41464115
break;
41474116

41484117
case T_ViewStmt:
41494118
{
41504119
ViewStmt *elp = (ViewStmt *) element;
41514120

4152-
setSchemaName(cxt.schemaname, &elp->view->schemaname);
4153-
4154-
/*
4155-
* XXX todo: deal with references between views
4156-
*/
4157-
cxt.views = lappend(cxt.views, element);
4121+
checkSchemaName(pstate, schemaName, elp->view);
4122+
elements = lappend(elements, element);
41584123
}
41594124
break;
41604125

41614126
case T_IndexStmt:
41624127
{
41634128
IndexStmt *elp = (IndexStmt *) element;
41644129

4165-
setSchemaName(cxt.schemaname, &elp->relation->schemaname);
4166-
cxt.indexes = lappend(cxt.indexes, element);
4130+
checkSchemaName(pstate, schemaName, elp->relation);
4131+
elements = lappend(elements, element);
41674132
}
41684133
break;
41694134

41704135
case T_CreateTrigStmt:
41714136
{
41724137
CreateTrigStmt *elp = (CreateTrigStmt *) element;
41734138

4174-
setSchemaName(cxt.schemaname, &elp->relation->schemaname);
4175-
cxt.triggers = lappend(cxt.triggers, element);
4139+
checkSchemaName(pstate, schemaName, elp->relation);
4140+
elements = lappend(elements, element);
41764141
}
41774142
break;
41784143

41794144
case T_GrantStmt:
4180-
cxt.grants = lappend(cxt.grants, element);
4145+
elements = lappend(elements, element);
41814146
break;
41824147

41834148
default:
@@ -4186,32 +4151,40 @@ transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
41864151
}
41874152
}
41884153

4189-
result = NIL;
4190-
result = list_concat(result, cxt.sequences);
4191-
result = list_concat(result, cxt.tables);
4192-
result = list_concat(result, cxt.views);
4193-
result = list_concat(result, cxt.indexes);
4194-
result = list_concat(result, cxt.triggers);
4195-
result = list_concat(result, cxt.grants);
4196-
4197-
return result;
4154+
return elements;
41984155
}
41994156

42004157
/*
4201-
* setSchemaName
4202-
* Set or check schema name in an element of a CREATE SCHEMA command
4158+
* checkSchemaName
4159+
* Check schema name in an element of a CREATE SCHEMA command
4160+
*
4161+
* It's okay if the command doesn't specify a target schema name, because
4162+
* CreateSchemaCommand will set up the default creation schema to be the
4163+
* new schema. But if a target schema name is given, it had better match.
4164+
* We also have to check that the command doesn't say CREATE TEMP, since
4165+
* that would likewise put the object into the wrong schema.
42034166
*/
42044167
static void
4205-
setSchemaName(const char *context_schema, char **stmt_schema_name)
4168+
checkSchemaName(ParseState *pstate, const char *context_schema,
4169+
RangeVar *relation)
42064170
{
4207-
if (*stmt_schema_name == NULL)
4208-
*stmt_schema_name = unconstify(char *, context_schema);
4209-
else if (strcmp(context_schema, *stmt_schema_name) != 0)
4171+
if (relation->schemaname != NULL &&
4172+
strcmp(context_schema, relation->schemaname) != 0)
42104173
ereport(ERROR,
4211-
(errcode(ERRCODE_INVALID_SCHEMA_DEFINITION),
4212-
errmsg("CREATE specifies a schema (%s) "
4174+
errcode(ERRCODE_INVALID_SCHEMA_DEFINITION),
4175+
errmsg("CREATE specifies a schema (%s) "
42134176
"different from the one being created (%s)",
4214-
*stmt_schema_name, context_schema)));
4177+
relation->schemaname, context_schema),
4178+
parser_errposition(pstate, relation->location));
4179+
4180+
if (relation->relpersistence == RELPERSISTENCE_TEMP)
4181+
{
4182+
/* spell this error the same as in RangeVarAdjustRelationPersistence */
4183+
ereport(ERROR,
4184+
errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4185+
errmsg("cannot create temporary relation in non-temporary schema"),
4186+
parser_errposition(pstate, relation->location));
4187+
}
42154188
}
42164189

42174190
/*

src/backend/tcop/utility.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1111,8 +1111,7 @@ ProcessUtilitySlow(ParseState *pstate,
11111111
* relation and attribute manipulation
11121112
*/
11131113
case T_CreateSchemaStmt:
1114-
CreateSchemaCommand((CreateSchemaStmt *) parsetree,
1115-
queryString,
1114+
CreateSchemaCommand(pstate, (CreateSchemaStmt *) parsetree,
11161115
pstmt->stmt_location,
11171116
pstmt->stmt_len);
11181117

src/include/commands/schemacmds.h

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,9 @@
1616
#define SCHEMACMDS_H
1717

1818
#include "catalog/objectaddress.h"
19-
#include "nodes/parsenodes.h"
20-
21-
extern Oid CreateSchemaCommand(CreateSchemaStmt *stmt,
22-
const char *queryString,
23-
int stmt_location, int stmt_len);
19+
#include "parser/parse_node.h"
2420

21+
extern Oid CreateSchemaCommand(ParseState *pstate, CreateSchemaStmt *stmt, int stmt_location, int stmt_len);
2522
extern ObjectAddress RenameSchema(const char *oldname, const char *newname);
2623
extern ObjectAddress AlterSchemaOwner(const char *name, Oid newOwnerId);
2724
extern void AlterSchemaOwner_oid(Oid schemaoid, Oid newOwnerId);

src/include/parser/parse_utilcmd.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
3030
const char *queryString);
3131
extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
3232
List **actions, Node **whereClause);
33-
extern List *transformCreateSchemaStmtElements(List *schemaElts,
33+
extern List *transformCreateSchemaStmtElements(ParseState *pstate,
34+
List *schemaElts,
3435
const char *schemaName);
3536
extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
3637
PartitionBoundSpec *spec);

0 commit comments

Comments
 (0)