From b6dae498dfa60c9d15abf791d51434e8d23e5cae Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 22 Oct 2026 13:19:47 -0400 Subject: [PATCH] Qualify unqualified names in CREATE SCHEMA elements again. Commit a9c350d9e stopped filling in the new schema's name, and the partial revert in 962e0a723 kept that, which was an error: it allows names that will be looked up rather than created to be resolved in pg_temp or pg_catalog, which they should not be. This for example allowed a CREATE INDEX to target a chance-match table name in pg_temp rather than the expected table in the new schema. Revert to the old approach of putting the new schema's name into each RangeVar. We can still keep the refactorization of dealing with RangeVars as such instead of working with char* pointers. Author: shihao zhong Reviewed-by: Tom Lane Discussion: https://postgr.es/m/CAGRkXqRMkfs25+7ReRkF4WCogEnD1k1=L=fmA_kxbtDfmhFn-g@mail.gmail.com Backpatch-through: 19 --- src/backend/parser/parse_utilcmd.c | 38 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index f0c7755b8fb..583346bf531 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -136,7 +136,7 @@ static void transformCheckConstraints(CreateStmtContext *cxt, static void transformConstraintAttrs(ParseState *pstate, List *constraintList); static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column); -static void checkSchemaNameRV(CreateSchemaStmtContext *cxt, RangeVar *relation); +static void setSchemaNameRV(CreateSchemaStmtContext *cxt, RangeVar *relation); static CreateStmt *transformCreateSchemaCreateTable(ParseState *pstate, CreateStmt *stmt, List **fk_elements); @@ -4127,7 +4127,7 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column) * transformCreateSchemaStmtElements - * analyzes the elements of a CREATE SCHEMA statement * - * This presently has two responsibilities. We verify that no subcommands + * This presently has two responsibilities. We ensure that no subcommands * are trying to create objects outside the new schema. We also attempt to * re-order the subcommands such that there are no forward references * (e.g. GRANT to a table created later in the list). Note that the logic @@ -4159,6 +4159,9 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, List *result; ListCell *elements; + /* We fill in schema names below, so work on a copy */ + schemaElts = copyObject(schemaElts); + cxt.pstate = pstate; cxt.schemaname = schemaName; cxt.sequences = NIL; @@ -4170,9 +4173,8 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, cxt.foreign_keys = NIL; /* - * Run through each schema element in the schema element list. Check - * target schema names, separate statements by type, and do preliminary - * analysis. + * Run through each schema element in the schema element list. Set target + * schema names, separate statements by type, and do preliminary analysis. */ foreach(elements, schemaElts) { @@ -4184,7 +4186,7 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, { CreateSeqStmt *elp = (CreateSeqStmt *) element; - checkSchemaNameRV(&cxt, elp->sequence); + setSchemaNameRV(&cxt, elp->sequence); cxt.sequences = lappend(cxt.sequences, element); } break; @@ -4193,7 +4195,7 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, { CreateStmt *elp = (CreateStmt *) element; - checkSchemaNameRV(&cxt, elp->relation); + setSchemaNameRV(&cxt, elp->relation); /* Pull out any foreign key clauses, add to fk_elements */ elp = transformCreateSchemaCreateTable(pstate, elp, @@ -4210,7 +4212,7 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, { ViewStmt *elp = (ViewStmt *) element; - checkSchemaNameRV(&cxt, elp->view); + setSchemaNameRV(&cxt, elp->view); /* * XXX todo: deal with references between views @@ -4223,7 +4225,7 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, { IndexStmt *elp = (IndexStmt *) element; - checkSchemaNameRV(&cxt, elp->relation); + setSchemaNameRV(&cxt, elp->relation); cxt.indexes = lappend(cxt.indexes, element); } break; @@ -4232,7 +4234,7 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, { CreateTrigStmt *elp = (CreateTrigStmt *) element; - checkSchemaNameRV(&cxt, elp->relation); + setSchemaNameRV(&cxt, elp->relation); cxt.triggers = lappend(cxt.triggers, element); } break; @@ -4260,21 +4262,21 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, } /* - * checkSchemaNameRV - * Check schema name in an element of a CREATE SCHEMA command, + * setSchemaNameRV + * Set or check schema name in an element of a CREATE SCHEMA command, * where the element's name is given by a RangeVar * - * It's okay if the command doesn't specify a target schema name, because - * CreateSchemaCommand will set up the default creation schema to be the - * new schema. But if a target schema name is given, it had better match. + * If the command doesn't specify a target schema name, fill in the new + * schema's name. But if a target schema name is given, it had better match. * We also have to check that the command doesn't say CREATE TEMP, since * that would likewise put the object into the wrong schema. */ static void -checkSchemaNameRV(CreateSchemaStmtContext *cxt, RangeVar *relation) +setSchemaNameRV(CreateSchemaStmtContext *cxt, RangeVar *relation) { - if (relation->schemaname != NULL && - strcmp(cxt->schemaname, relation->schemaname) != 0) + if (relation->schemaname == NULL) + relation->schemaname = pstrdup(cxt->schemaname); + else if (strcmp(cxt->schemaname, relation->schemaname) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_SCHEMA_DEFINITION), errmsg("CREATE specifies a schema (%s) "