From 565132d93cf189077d19dba48240002306f62cd1 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 30 Nov 2024 18:11:04 -0500
Subject: [PATCH v2] 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.

Discussion: https://postgr.es/m/1075425.1732993688@sss.pgh.pa.us
---
 doc/src/sgml/ref/create_schema.sgml         |  10 +-
 src/backend/commands/extension.c            |   9 +-
 src/backend/commands/schemacmds.c           |  22 ++--
 src/backend/parser/parse_utilcmd.c          | 131 ++++++++------------
 src/backend/tcop/utility.c                  |   7 +-
 src/include/commands/schemacmds.h           |   7 +-
 src/include/parser/parse_utilcmd.h          |   3 +-
 src/test/regress/expected/create_schema.out |  30 +++++
 src/test/regress/expected/create_table.out  |   9 ++
 src/test/regress/expected/create_view.out   |   2 +
 src/test/regress/expected/event_trigger.out |   2 +-
 src/test/regress/expected/namespace.out     |  19 ++-
 src/test/regress/sql/create_table.sql       |   6 +
 src/test/regress/sql/namespace.sql          |  22 +++-
 14 files changed, 161 insertions(+), 118 deletions(-)

diff --git a/doc/src/sgml/ref/create_schema.sgml b/doc/src/sgml/ref/create_schema.sgml
index ed69298ccc6..625793a6b67 100644
--- a/doc/src/sgml/ref/create_schema.sgml
+++ b/doc/src/sgml/ref/create_schema.sgml
@@ -193,12 +193,10 @@ CREATE VIEW hollywood.winners AS
   </para>
 
   <para>
-   The SQL standard specifies that the subcommands in <command>CREATE
-   SCHEMA</command> can appear in any order.  The present
-   <productname>PostgreSQL</productname> implementation does not
-   handle all cases of forward references in subcommands; it might
-   sometimes be necessary to reorder the subcommands in order to avoid
-   forward references.
+   <productname>PostgreSQL</productname> executes the subcommands
+   in <command>CREATE SCHEMA</command> in the order given.  Other
+   implementations may try to rearrange the subcommands into dependency
+   order, but that is hard if not impossible to do correctly.
   </para>
 
   <para>
diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c
index af6bd8ff426..a2eb42dc7fb 100644
--- a/src/backend/commands/extension.c
+++ b/src/backend/commands/extension.c
@@ -1687,14 +1687,19 @@ CreateExtensionInternal(char *extensionName,
 
 		if (!OidIsValid(schemaOid))
 		{
+			ParseState *pstate = make_parsestate(NULL);
 			CreateSchemaStmt *csstmt = makeNode(CreateSchemaStmt);
 
+			pstate->p_sourcetext = "(generated CREATE SCHEMA command)";
+			pstate->p_stmt_location = -1;
+			pstate->p_stmt_len = -1;
+
 			csstmt->schemaname = schemaName;
 			csstmt->authrole = NULL;	/* will be created by current user */
 			csstmt->schemaElts = NIL;
 			csstmt->if_not_exists = false;
-			CreateSchemaCommand(csstmt, "(generated CREATE SCHEMA command)",
-								-1, -1);
+
+			CreateSchemaCommand(pstate, csstmt);
 
 			/*
 			 * CreateSchemaCommand includes CommandCounterIncrement, so new
diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c
index 233f8ad1d44..4470b9a4023 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -42,15 +42,14 @@ static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid newOwnerI
 /*
  * CREATE SCHEMA
  *
- * Note: caller should pass in location information for the whole
+ * Note: pstate should pass in location information for the whole
  * CREATE SCHEMA statement, which in turn we pass down as the location
  * of the component commands.  This comports with our general plan of
  * reporting location/len for the whole command even when executing
  * a subquery.
  */
 Oid
-CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
-					int stmt_location, int stmt_len)
+CreateSchemaCommand(ParseState *pstate, CreateSchemaStmt *stmt)
 {
 	const char *schemaName = stmt->schemaname;
 	Oid			namespaceId;
@@ -189,12 +188,13 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
 
 	/*
 	 * Examine the list of commands embedded in the CREATE SCHEMA command, and
-	 * reorganize them into a sequentially executable order with no forward
-	 * references.  Note that the result is still a list of raw parsetrees ---
-	 * we cannot, in general, run parse analysis on one statement until we
-	 * have actually executed the prior ones.
+	 * do preliminary transformations (mostly, verify that none are trying to
+	 * create objects outside the new schema).  Note that the result is still
+	 * a list of raw parsetrees --- we cannot, in general, run parse analysis
+	 * on one statement until we have actually executed the prior ones.
 	 */
-	parsetree_list = transformCreateSchemaStmtElements(stmt->schemaElts,
+	parsetree_list = transformCreateSchemaStmtElements(pstate,
+													   stmt->schemaElts,
 													   schemaName);
 
 	/*
@@ -213,12 +213,12 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString,
 		wrapper->commandType = CMD_UTILITY;
 		wrapper->canSetTag = false;
 		wrapper->utilityStmt = stmt;
-		wrapper->stmt_location = stmt_location;
-		wrapper->stmt_len = stmt_len;
+		wrapper->stmt_location = pstate->p_stmt_location;
+		wrapper->stmt_len = pstate->p_stmt_len;
 
 		/* do this step */
 		ProcessUtility(wrapper,
-					   queryString,
+					   pstate->p_sourcetext,
 					   false,
 					   PROCESS_UTILITY_SUBCOMMAND,
 					   NULL,
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 0f324ee4e31..6d00643f34b 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -95,18 +95,6 @@ typedef struct
 	bool		ofType;			/* true if statement contains OF typename */
 } CreateStmtContext;
 
-/* State shared by transformCreateSchemaStmtElements and its subroutines */
-typedef struct
-{
-	const char *schemaname;		/* name of schema */
-	List	   *sequences;		/* CREATE SEQUENCE items */
-	List	   *tables;			/* CREATE TABLE items */
-	List	   *views;			/* CREATE VIEW items */
-	List	   *indexes;		/* CREATE INDEX items */
-	List	   *triggers;		/* CREATE TRIGGER items */
-	List	   *grants;			/* GRANT items */
-} CreateSchemaStmtContext;
-
 
 static void transformColumnDefinition(CreateStmtContext *cxt,
 									  ColumnDef *column);
@@ -133,7 +121,8 @@ static void transformCheckConstraints(CreateStmtContext *cxt,
 static void transformConstraintAttrs(CreateStmtContext *cxt,
 									 List *constraintList);
 static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
-static void setSchemaName(const char *context_schema, char **stmt_schema_name);
+static void checkSchemaName(ParseState *pstate, const char *context_schema,
+							RangeVar *relation);
 static void transformPartitionCmd(CreateStmtContext *cxt, PartitionCmd *cmd);
 static List *transformPartitionRangeBounds(ParseState *pstate, List *blist,
 										   Relation parent);
@@ -4002,51 +3991,35 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column)
  * transformCreateSchemaStmtElements -
  *	  analyzes the elements of a CREATE SCHEMA statement
  *
- * Split the schema element list from a CREATE SCHEMA statement into
- * individual commands and place them in the result list in an order
- * such that there are no forward references (e.g. GRANT to a table
- * created later in the list). Note that the logic we use for determining
- * forward references is presently quite incomplete.
+ * This is now somewhat vestigial: its only real responsibility is to complain
+ * if any of the elements are trying to create objects outside the new schema.
+ * We used to try to re-order the commands in a way that would work even if
+ * the user-written order would not, but that's too hard (perhaps impossible)
+ * to do correctly with not-yet-parse-analyzed commands.  Now we'll just
+ * execute the elements in the order given.
  *
  * "schemaName" is the name of the schema that will be used for the creation
- * of the objects listed, that may be compiled from the schema name defined
+ * of the objects listed.  It may be obtained from the schema name defined
  * in the statement or a role specification.
  *
- * SQL also allows constraints to make forward references, so thumb through
- * the table columns and move forward references to a posterior alter-table
- * command.
- *
  * The result is a list of parse nodes that still need to be analyzed ---
  * but we can't analyze the later commands until we've executed the earlier
  * ones, because of possible inter-object references.
- *
- * Note: this breaks the rules a little bit by modifying schema-name fields
- * within passed-in structs.  However, the transformation would be the same
- * if done over, so it should be all right to scribble on the input to this
- * extent.
  */
 List *
-transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
+transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts,
+								  const char *schemaName)
 {
-	CreateSchemaStmtContext cxt;
-	List	   *result;
-	ListCell   *elements;
-
-	cxt.schemaname = schemaName;
-	cxt.sequences = NIL;
-	cxt.tables = NIL;
-	cxt.views = NIL;
-	cxt.indexes = NIL;
-	cxt.triggers = NIL;
-	cxt.grants = NIL;
+	List	   *elements = NIL;
+	ListCell   *lc;
 
 	/*
-	 * Run through each schema element in the schema element list. Separate
-	 * statements by type, and do preliminary analysis.
+	 * Run through each schema element in the schema element list.  Check
+	 * target schema names, and collect the list of actions to be done.
 	 */
-	foreach(elements, schemaElts)
+	foreach(lc, schemaElts)
 	{
-		Node	   *element = lfirst(elements);
+		Node	   *element = lfirst(lc);
 
 		switch (nodeTag(element))
 		{
@@ -4054,8 +4027,8 @@ transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
 				{
 					CreateSeqStmt *elp = (CreateSeqStmt *) element;
 
-					setSchemaName(cxt.schemaname, &elp->sequence->schemaname);
-					cxt.sequences = lappend(cxt.sequences, element);
+					checkSchemaName(pstate, schemaName, elp->sequence);
+					elements = lappend(elements, element);
 				}
 				break;
 
@@ -4063,12 +4036,8 @@ transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
 				{
 					CreateStmt *elp = (CreateStmt *) element;
 
-					setSchemaName(cxt.schemaname, &elp->relation->schemaname);
-
-					/*
-					 * XXX todo: deal with constraints
-					 */
-					cxt.tables = lappend(cxt.tables, element);
+					checkSchemaName(pstate, schemaName, elp->relation);
+					elements = lappend(elements, element);
 				}
 				break;
 
@@ -4076,12 +4045,8 @@ transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
 				{
 					ViewStmt   *elp = (ViewStmt *) element;
 
-					setSchemaName(cxt.schemaname, &elp->view->schemaname);
-
-					/*
-					 * XXX todo: deal with references between views
-					 */
-					cxt.views = lappend(cxt.views, element);
+					checkSchemaName(pstate, schemaName, elp->view);
+					elements = lappend(elements, element);
 				}
 				break;
 
@@ -4089,8 +4054,8 @@ transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
 				{
 					IndexStmt  *elp = (IndexStmt *) element;
 
-					setSchemaName(cxt.schemaname, &elp->relation->schemaname);
-					cxt.indexes = lappend(cxt.indexes, element);
+					checkSchemaName(pstate, schemaName, elp->relation);
+					elements = lappend(elements, element);
 				}
 				break;
 
@@ -4098,13 +4063,13 @@ transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
 				{
 					CreateTrigStmt *elp = (CreateTrigStmt *) element;
 
-					setSchemaName(cxt.schemaname, &elp->relation->schemaname);
-					cxt.triggers = lappend(cxt.triggers, element);
+					checkSchemaName(pstate, schemaName, elp->relation);
+					elements = lappend(elements, element);
 				}
 				break;
 
 			case T_GrantStmt:
-				cxt.grants = lappend(cxt.grants, element);
+				elements = lappend(elements, element);
 				break;
 
 			default:
@@ -4113,32 +4078,40 @@ transformCreateSchemaStmtElements(List *schemaElts, const char *schemaName)
 		}
 	}
 
-	result = NIL;
-	result = list_concat(result, cxt.sequences);
-	result = list_concat(result, cxt.tables);
-	result = list_concat(result, cxt.views);
-	result = list_concat(result, cxt.indexes);
-	result = list_concat(result, cxt.triggers);
-	result = list_concat(result, cxt.grants);
-
-	return result;
+	return elements;
 }
 
 /*
- * setSchemaName
- *		Set or check schema name in an element of a CREATE SCHEMA command
+ * checkSchemaName
+ *		Check schema name in an element of a CREATE SCHEMA command
+ *
+ * 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.
+ * 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
-setSchemaName(const char *context_schema, char **stmt_schema_name)
+checkSchemaName(ParseState *pstate, const char *context_schema,
+				RangeVar *relation)
 {
-	if (*stmt_schema_name == NULL)
-		*stmt_schema_name = unconstify(char *, context_schema);
-	else if (strcmp(context_schema, *stmt_schema_name) != 0)
+	if (relation->schemaname != NULL &&
+		strcmp(context_schema, relation->schemaname) != 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_SCHEMA_DEFINITION),
 				 errmsg("CREATE specifies a schema (%s) "
 						"different from the one being created (%s)",
-						*stmt_schema_name, context_schema)));
+						relation->schemaname, context_schema),
+				 parser_errposition(pstate, relation->location)));
+
+	if (relation->relpersistence == RELPERSISTENCE_TEMP)
+	{
+		/* spell this error the same as in RangeVarAdjustRelationPersistence */
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+				 errmsg("cannot create temporary relation in non-temporary schema"),
+				 parser_errposition(pstate, relation->location)));
+	}
 }
 
 /*
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f28bf371059..ad0a3146309 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -587,6 +587,8 @@ standard_ProcessUtility(PlannedStmt *pstmt,
 
 	pstate = make_parsestate(NULL);
 	pstate->p_sourcetext = queryString;
+	pstate->p_stmt_location = pstmt->stmt_location;
+	pstate->p_stmt_len = pstmt->stmt_len;
 	pstate->p_queryEnv = queryEnv;
 
 	switch (nodeTag(parsetree))
@@ -1121,10 +1123,7 @@ ProcessUtilitySlow(ParseState *pstate,
 				 * relation and attribute manipulation
 				 */
 			case T_CreateSchemaStmt:
-				CreateSchemaCommand((CreateSchemaStmt *) parsetree,
-									queryString,
-									pstmt->stmt_location,
-									pstmt->stmt_len);
+				CreateSchemaCommand(pstate, (CreateSchemaStmt *) parsetree);
 
 				/*
 				 * EventTriggerCollectSimpleCommand called by
diff --git a/src/include/commands/schemacmds.h b/src/include/commands/schemacmds.h
index 5598dfa5d76..e6861994d03 100644
--- a/src/include/commands/schemacmds.h
+++ b/src/include/commands/schemacmds.h
@@ -16,12 +16,9 @@
 #define SCHEMACMDS_H
 
 #include "catalog/objectaddress.h"
-#include "nodes/parsenodes.h"
-
-extern Oid	CreateSchemaCommand(CreateSchemaStmt *stmt,
-								const char *queryString,
-								int stmt_location, int stmt_len);
+#include "parser/parse_node.h"
 
+extern Oid	CreateSchemaCommand(ParseState *pstate, CreateSchemaStmt *stmt);
 extern ObjectAddress RenameSchema(const char *oldname, const char *newname);
 extern ObjectAddress AlterSchemaOwner(const char *name, Oid newOwnerId);
 extern void AlterSchemaOwner_oid(Oid schemaoid, Oid newOwnerId);
diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h
index 14065894779..5d22ea988c5 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -30,7 +30,8 @@ extern CreateStatsStmt *transformStatsStmt(Oid relid, CreateStatsStmt *stmt,
 										   const char *queryString);
 extern void transformRuleStmt(RuleStmt *stmt, const char *queryString,
 							  List **actions, Node **whereClause);
-extern List *transformCreateSchemaStmtElements(List *schemaElts,
+extern List *transformCreateSchemaStmtElements(ParseState *pstate,
+											   List *schemaElts,
 											   const char *schemaName);
 extern PartitionBoundSpec *transformPartitionBound(ParseState *pstate, Relation parent,
 												   PartitionBoundSpec *spec);
diff --git a/src/test/regress/expected/create_schema.out b/src/test/regress/expected/create_schema.out
index 93302a07efc..554f0f3c50b 100644
--- a/src/test/regress/expected/create_schema.out
+++ b/src/test/regress/expected/create_schema.out
@@ -9,54 +9,84 @@ CREATE ROLE regress_create_schema_role SUPERUSER;
 CREATE SCHEMA AUTHORIZATION regress_create_schema_role
   CREATE SEQUENCE schema_not_existing.seq;
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE SEQUENCE schema_not_existing.seq;
+                          ^
 CREATE SCHEMA AUTHORIZATION regress_create_schema_role
   CREATE TABLE schema_not_existing.tab (id int);
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE TABLE schema_not_existing.tab (id int);
+                       ^
 CREATE SCHEMA AUTHORIZATION regress_create_schema_role
   CREATE VIEW schema_not_existing.view AS SELECT 1;
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE VIEW schema_not_existing.view AS SELECT 1;
+                      ^
 CREATE SCHEMA AUTHORIZATION regress_create_schema_role
   CREATE INDEX ON schema_not_existing.tab (id);
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE INDEX ON schema_not_existing.tab (id);
+                          ^
 CREATE SCHEMA AUTHORIZATION regress_create_schema_role
   CREATE TRIGGER schema_trig BEFORE INSERT ON schema_not_existing.tab
   EXECUTE FUNCTION schema_trig.no_func();
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE TRIGGER schema_trig BEFORE INSERT ON schema_not_exi...
+                                                      ^
 -- Again, with a role specification and no schema names.
 SET ROLE regress_create_schema_role;
 CREATE SCHEMA AUTHORIZATION CURRENT_ROLE
   CREATE SEQUENCE schema_not_existing.seq;
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE SEQUENCE schema_not_existing.seq;
+                          ^
 CREATE SCHEMA AUTHORIZATION CURRENT_ROLE
   CREATE TABLE schema_not_existing.tab (id int);
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE TABLE schema_not_existing.tab (id int);
+                       ^
 CREATE SCHEMA AUTHORIZATION CURRENT_ROLE
   CREATE VIEW schema_not_existing.view AS SELECT 1;
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE VIEW schema_not_existing.view AS SELECT 1;
+                      ^
 CREATE SCHEMA AUTHORIZATION CURRENT_ROLE
   CREATE INDEX ON schema_not_existing.tab (id);
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE INDEX ON schema_not_existing.tab (id);
+                          ^
 CREATE SCHEMA AUTHORIZATION CURRENT_ROLE
   CREATE TRIGGER schema_trig BEFORE INSERT ON schema_not_existing.tab
   EXECUTE FUNCTION schema_trig.no_func();
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_create_schema_role)
+LINE 2:   CREATE TRIGGER schema_trig BEFORE INSERT ON schema_not_exi...
+                                                      ^
 -- Again, with a schema name and a role specification.
 CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE
   CREATE SEQUENCE schema_not_existing.seq;
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_schema_1)
+LINE 2:   CREATE SEQUENCE schema_not_existing.seq;
+                          ^
 CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE
   CREATE TABLE schema_not_existing.tab (id int);
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_schema_1)
+LINE 2:   CREATE TABLE schema_not_existing.tab (id int);
+                       ^
 CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE
   CREATE VIEW schema_not_existing.view AS SELECT 1;
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_schema_1)
+LINE 2:   CREATE VIEW schema_not_existing.view AS SELECT 1;
+                      ^
 CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE
   CREATE INDEX ON schema_not_existing.tab (id);
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_schema_1)
+LINE 2:   CREATE INDEX ON schema_not_existing.tab (id);
+                          ^
 CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE
   CREATE TRIGGER schema_trig BEFORE INSERT ON schema_not_existing.tab
   EXECUTE FUNCTION schema_trig.no_func();
 ERROR:  CREATE specifies a schema (schema_not_existing) different from the one being created (regress_schema_1)
+LINE 2:   CREATE TRIGGER schema_trig BEFORE INSERT ON schema_not_exi...
+                                                      ^
 RESET ROLE;
 -- Cases where the schema creation succeeds.
 -- The schema created matches the role name.
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 76604705a93..e90401c4c69 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -49,6 +49,15 @@ CREATE TEMP TABLE public.temp_to_perm (a int primary key);		-- not OK
 ERROR:  cannot create temporary relation in non-temporary schema
 LINE 1: CREATE TEMP TABLE public.temp_to_perm (a int primary key);
                           ^
+-- test error and error position with temporary table created within
+-- a non-temporary schema.
+CREATE SCHEMA test_ns_schema_tmp
+       CREATE TEMP TABLE abc (
+              a serial
+       );
+ERROR:  cannot create temporary relation in non-temporary schema
+LINE 2:        CREATE TEMP TABLE abc (
+                                 ^
 DROP TABLE unlogged1, public.unlogged2;
 CREATE UNLOGGED TABLE unlogged1 (a int) PARTITION BY RANGE (a); -- fail
 ERROR:  partitioned tables cannot be unlogged
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index f551624afb3..b305ceea033 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -126,6 +126,8 @@ ERROR:  cannot create temporary relation in non-temporary schema
 CREATE SCHEMA test_view_schema
     CREATE TEMP VIEW testview AS SELECT 1;
 ERROR:  cannot create temporary relation in non-temporary schema
+LINE 2:     CREATE TEMP VIEW testview AS SELECT 1;
+                             ^
 -- joins: if any of the join relations are temporary, the view
 -- should also be temporary
 -- should be non-temp
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 7b2198eac6f..bafde8706fe 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -411,12 +411,12 @@ NOTICE:  END: command_tag=CREATE TABLE type=table identity=evttrig.one
 NOTICE:  END: command_tag=CREATE INDEX type=index identity=evttrig.one_pkey
 NOTICE:  END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.one_col_a_seq
 NOTICE:  END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.one_col_c_seq
+NOTICE:  END: command_tag=CREATE INDEX type=index identity=evttrig.one_idx
 NOTICE:  END: command_tag=CREATE TABLE type=table identity=evttrig.two
 NOTICE:  END: command_tag=ALTER TABLE type=table identity=evttrig.two
 NOTICE:  END: command_tag=CREATE SEQUENCE type=sequence identity=evttrig.id_col_d_seq
 NOTICE:  END: command_tag=CREATE TABLE type=table identity=evttrig.id
 NOTICE:  END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.id_col_d_seq
-NOTICE:  END: command_tag=CREATE INDEX type=index identity=evttrig.one_idx
 -- Partitioned tables with a partitioned index
 CREATE TABLE evttrig.parted (
     id int PRIMARY KEY)
diff --git a/src/test/regress/expected/namespace.out b/src/test/regress/expected/namespace.out
index dbbda72d395..452d5689b22 100644
--- a/src/test/regress/expected/namespace.out
+++ b/src/test/regress/expected/namespace.out
@@ -10,13 +10,14 @@ SELECT pg_catalog.set_config('search_path', ' ', false);
 (1 row)
 
 CREATE SCHEMA test_ns_schema_1
-       CREATE UNIQUE INDEX abc_a_idx ON abc (a)
-       CREATE VIEW abc_view AS
-              SELECT a+1 AS a, b+1 AS b FROM abc
        CREATE TABLE abc (
               a serial,
               b int UNIQUE
-       );
+       )
+       CREATE UNIQUE INDEX abc_a_idx ON abc (a)
+       CREATE VIEW abc_view AS
+              SELECT a+1 AS a, b+1 AS b FROM abc
+;
 -- verify that the correct search_path restored on abort
 SET search_path to public;
 BEGIN;
@@ -54,6 +55,16 @@ SHOW search_path;
 
 DROP SCHEMA test_ns_schema_2 CASCADE;
 NOTICE:  drop cascades to view test_ns_schema_2.abc_view
+-- test error with wrong object creation order.
+CREATE SCHEMA test_ns_schema_3 
+       CREATE VIEW abcd_view AS
+              SELECT a FROM abcd
+       CREATE TABLE abcd (
+              a serial
+       );
+ERROR:  relation "abcd" does not exist
+LINE 3:               SELECT a FROM abcd
+                                    ^
 -- verify that the objects were created
 SELECT COUNT(*) FROM pg_class WHERE relnamespace =
     (SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1');
diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql
index 37a227148e9..1e9237fc099 100644
--- a/src/test/regress/sql/create_table.sql
+++ b/src/test/regress/sql/create_table.sql
@@ -28,6 +28,12 @@ CREATE TABLE pg_temp.implicitly_temp (a int primary key);		-- OK
 CREATE TEMP TABLE explicitly_temp (a int primary key);			-- also OK
 CREATE TEMP TABLE pg_temp.doubly_temp (a int primary key);		-- also OK
 CREATE TEMP TABLE public.temp_to_perm (a int primary key);		-- not OK
+-- test error and error position with temporary table created within
+-- a non-temporary schema.
+CREATE SCHEMA test_ns_schema_tmp
+       CREATE TEMP TABLE abc (
+              a serial
+       );
 DROP TABLE unlogged1, public.unlogged2;
 
 CREATE UNLOGGED TABLE unlogged1 (a int) PARTITION BY RANGE (a); -- fail
diff --git a/src/test/regress/sql/namespace.sql b/src/test/regress/sql/namespace.sql
index 306cdc2d8c6..66aa1ad6eba 100644
--- a/src/test/regress/sql/namespace.sql
+++ b/src/test/regress/sql/namespace.sql
@@ -7,15 +7,17 @@
 SELECT pg_catalog.set_config('search_path', ' ', false);
 
 CREATE SCHEMA test_ns_schema_1
-       CREATE UNIQUE INDEX abc_a_idx ON abc (a)
-
-       CREATE VIEW abc_view AS
-              SELECT a+1 AS a, b+1 AS b FROM abc
 
        CREATE TABLE abc (
               a serial,
               b int UNIQUE
-       );
+       )
+
+       CREATE UNIQUE INDEX abc_a_idx ON abc (a)
+
+       CREATE VIEW abc_view AS
+              SELECT a+1 AS a, b+1 AS b FROM abc
+;
 
 -- verify that the correct search_path restored on abort
 SET search_path to public;
@@ -37,6 +39,16 @@ COMMIT;
 SHOW search_path;
 DROP SCHEMA test_ns_schema_2 CASCADE;
 
+-- test error with wrong object creation order.
+CREATE SCHEMA test_ns_schema_3 
+
+       CREATE VIEW abcd_view AS
+              SELECT a FROM abcd
+
+       CREATE TABLE abcd (
+              a serial
+       );
+
 -- verify that the objects were created
 SELECT COUNT(*) FROM pg_class WHERE relnamespace =
     (SELECT oid FROM pg_namespace WHERE nspname = 'test_ns_schema_1');
-- 
2.34.1

