From: Tom Lane Date: Mon, 6 Apr 2026 18:52:28 +0000 (-0400) Subject: Execute foreign key constraints in CREATE SCHEMA at the end. X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=404db8f9edbb291bcc31542d488fb20f6a965798;p=thirdparty%2Fpostgresql.git Execute foreign key constraints in CREATE SCHEMA at the end. The previous patch simplified CREATE SCHEMA's behavior to "execute all subcommands in the order they are written". However, that's a bit too simple, as the spec clearly requires forward references in foreign key constraint clauses to work, see feature F311-01. (Most other SQL implementations seem to read more into the spec than that, but it's not clear that there's justification for more in the text, and this is the only case that doesn't introduce unresolvable issues.) We never implemented that before, but let's do so now. To fix it, transform FOREIGN KEY clauses into ALTER TABLE ... ADD FOREIGN KEY commands and append them to the end of the CREATE SCHEMA's subcommand list. This works because the foreign key constraints are independent and don't affect any other DDL that might be in CREATE SCHEMA. For simplicity, we do this for all FOREIGN KEY clauses even if they would have worked where they were. Author: Jian He Co-authored-by: Tom Lane Discussion: https://postgr.es/m/1075425.1732993688@sss.pgh.pa.us --- diff --git a/doc/src/sgml/ref/create_schema.sgml b/doc/src/sgml/ref/create_schema.sgml index 9e6f0e24339..96bc496e777 100644 --- a/doc/src/sgml/ref/create_schema.sgml +++ b/doc/src/sgml/ref/create_schema.sgml @@ -135,6 +135,9 @@ CREATE SCHEMA IF NOT EXISTS AUTHORIZATION role_sp The schema_element subcommands, if any, are executed in the order they are written. + An exception is that foreign key constraint clauses in CREATE + TABLE subcommands are postponed and added at the end. + This allows circular foreign key references, which are sometimes useful. diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 151d9731273..b0c3407b23d 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -122,11 +122,14 @@ static void transformFKConstraints(CreateStmtContext *cxt, bool isAddConstraint); static void transformCheckConstraints(CreateStmtContext *cxt, bool skipValidation); -static void transformConstraintAttrs(CreateStmtContext *cxt, +static void transformConstraintAttrs(ParseState *pstate, List *constraintList); static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column); static void checkSchemaNameRV(ParseState *pstate, const char *context_schema, RangeVar *relation); +static CreateStmt *transformCreateSchemaCreateTable(ParseState *pstate, + CreateStmt *stmt, + List **fk_elements); static void transformPartitionCmd(CreateStmtContext *cxt, PartitionBoundSpec *bound); static List *transformPartitionRangeBounds(ParseState *pstate, List *blist, Relation parent); @@ -693,7 +696,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) } /* Process column constraints, if any... */ - transformConstraintAttrs(cxt, column->constraints); + transformConstraintAttrs(cxt->pstate, column->constraints); /* * First, scan the column's constraints to see if a not-null constraint @@ -4194,9 +4197,12 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, * NOTE: currently, attributes are only supported for FOREIGN KEY, UNIQUE, * EXCLUSION, and PRIMARY KEY constraints, but someday they ought to be * supported for other constraint types. + * + * NOTE: this must be idempotent in non-error cases; see + * transformCreateSchemaCreateTable. */ static void -transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) +transformConstraintAttrs(ParseState *pstate, List *constraintList) { Constraint *lastprimarycon = NULL; bool saw_deferrability = false; @@ -4225,12 +4231,12 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("misplaced DEFERRABLE clause"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); if (saw_deferrability) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); saw_deferrability = true; lastprimarycon->deferrable = true; break; @@ -4240,12 +4246,12 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("misplaced NOT DEFERRABLE clause"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); if (saw_deferrability) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple DEFERRABLE/NOT DEFERRABLE clauses not allowed"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); saw_deferrability = true; lastprimarycon->deferrable = false; if (saw_initially && @@ -4253,7 +4259,7 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); break; case CONSTR_ATTR_DEFERRED: @@ -4261,12 +4267,12 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("misplaced INITIALLY DEFERRED clause"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); if (saw_initially) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); saw_initially = true; lastprimarycon->initdeferred = true; @@ -4279,7 +4285,7 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("constraint declared INITIALLY DEFERRED must be DEFERRABLE"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); break; case CONSTR_ATTR_IMMEDIATE: @@ -4287,12 +4293,12 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("misplaced INITIALLY IMMEDIATE clause"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); if (saw_initially) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple INITIALLY IMMEDIATE/DEFERRED clauses not allowed"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); saw_initially = true; lastprimarycon->initdeferred = false; break; @@ -4304,12 +4310,12 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("misplaced ENFORCED clause"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); if (saw_enforced) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple ENFORCED/NOT ENFORCED clauses not allowed"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); saw_enforced = true; lastprimarycon->is_enforced = true; break; @@ -4321,12 +4327,12 @@ transformConstraintAttrs(CreateStmtContext *cxt, List *constraintList) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("misplaced NOT ENFORCED clause"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); if (saw_enforced) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("multiple ENFORCED/NOT ENFORCED clauses not allowed"), - parser_errposition(cxt->pstate, con->location))); + parser_errposition(pstate, con->location))); saw_enforced = true; lastprimarycon->is_enforced = false; @@ -4384,12 +4390,17 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column) * transformCreateSchemaStmtElements - * analyzes the elements of a CREATE SCHEMA statement * - * 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. + * This presently has two responsibilities. We verify that no subcommands are + * trying to create objects outside the new schema. We also pull out any + * foreign-key constraint clauses embedded in CREATE TABLE subcommands, and + * convert them to ALTER TABLE ADD CONSTRAINT commands appended to the list. + * This supports forward references in foreign keys, which is required by the + * SQL standard. + * * 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. + * execute the elements in the order given, except for foreign keys. * * "schemaName" is the name of the schema that will be used for the creation * of the objects listed. It may be obtained from the schema name defined @@ -4398,12 +4409,17 @@ transformColumnType(CreateStmtContext *cxt, ColumnDef *column) * 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 it's important that we not modify the input data structure. We create + * a new result List, and we copy any CREATE TABLE subcommands that we might + * modify. */ List * transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, const char *schemaName) { List *elements = NIL; + List *fk_elements = NIL; ListCell *lc; /* @@ -4430,7 +4446,11 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, CreateStmt *elp = (CreateStmt *) element; checkSchemaNameRV(pstate, schemaName, elp->relation); - elements = lappend(elements, element); + /* Pull out any foreign key clauses, add to fk_elements */ + elp = transformCreateSchemaCreateTable(pstate, + elp, + &fk_elements); + elements = lappend(elements, elp); } break; @@ -4471,7 +4491,7 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, } } - return elements; + return list_concat(elements, fk_elements); } /* @@ -4508,6 +4528,161 @@ checkSchemaNameRV(ParseState *pstate, const char *context_schema, } } +/* + * transformCreateSchemaCreateTable + * Process one CreateStmt for transformCreateSchemaStmtElements. + * + * We remove any foreign-key clauses in the statement and convert them into + * ALTER TABLE commands, which we append to *fk_elements. + */ +static CreateStmt * +transformCreateSchemaCreateTable(ParseState *pstate, + CreateStmt *stmt, + List **fk_elements) +{ + CreateStmt *newstmt; + List *newElts = NIL; + ListCell *lc; + + /* + * Flat-copy the CreateStmt node, allowing us to replace its tableElts + * list without damaging the input data structure. Most sub-nodes will be + * shared with the input, though. + */ + newstmt = makeNode(CreateStmt); + memcpy(newstmt, stmt, sizeof(CreateStmt)); + + /* Scan for foreign-key constraints */ + foreach(lc, stmt->tableElts) + { + Node *element = lfirst(lc); + AlterTableStmt *alterstmt; + AlterTableCmd *altercmd; + + if (IsA(element, Constraint)) + { + Constraint *constr = (Constraint *) element; + + if (constr->contype != CONSTR_FOREIGN) + { + /* Other constraint types pass through unchanged */ + newElts = lappend(newElts, constr); + continue; + } + + /* Make it into an ALTER TABLE ADD CONSTRAINT command */ + altercmd = makeNode(AlterTableCmd); + altercmd->subtype = AT_AddConstraint; + altercmd->name = NULL; + altercmd->def = (Node *) copyObject(constr); + + alterstmt = makeNode(AlterTableStmt); + alterstmt->relation = copyObject(stmt->relation); + alterstmt->cmds = list_make1(altercmd); + alterstmt->objtype = OBJECT_TABLE; + + *fk_elements = lappend(*fk_elements, alterstmt); + } + else if (IsA(element, ColumnDef)) + { + ColumnDef *entry = (ColumnDef *) element; + ColumnDef *newentry; + List *entryconstraints; + bool afterFK = false; + + /* + * We must preprocess the list of column constraints to attach + * attributes such as DEFERRED to the appropriate constraint node. + * Do this on a copy. (But execution of the CreateStmt will run + * transformConstraintAttrs on the copy, so we are nonetheless + * relying on transformConstraintAttrs to be idempotent.) + */ + entryconstraints = copyObject(entry->constraints); + transformConstraintAttrs(pstate, entryconstraints); + + /* Scan the column constraints ... */ + foreach_node(Constraint, colconstr, entryconstraints) + { + switch (colconstr->contype) + { + case CONSTR_FOREIGN: + /* colconstr is already a copy, OK to modify */ + colconstr->fk_attrs = list_make1(makeString(entry->colname)); + + /* Make it into an ALTER TABLE ADD CONSTRAINT command */ + altercmd = makeNode(AlterTableCmd); + altercmd->subtype = AT_AddConstraint; + altercmd->name = NULL; + altercmd->def = (Node *) colconstr; + + alterstmt = makeNode(AlterTableStmt); + alterstmt->relation = copyObject(stmt->relation); + alterstmt->cmds = list_make1(altercmd); + alterstmt->objtype = OBJECT_TABLE; + + *fk_elements = lappend(*fk_elements, alterstmt); + + /* Remove the Constraint node from entryconstraints */ + entryconstraints = + foreach_delete_current(entryconstraints, colconstr); + + /* + * Immediately-following attribute constraints should + * be dropped, too. + */ + afterFK = true; + break; + + /* + * Column constraint lists separate a Constraint node + * from its attributes (e.g. NOT ENFORCED); so a + * column-level foreign key constraint may be + * represented by multiple Constraint nodes. After + * transformConstraintAttrs, the foreign key + * Constraint node contains all required information, + * making it okay to put into *fk_elements as a + * stand-alone Constraint. But since we removed the + * foreign key Constraint node from entryconstraints, + * we must remove any dependent attribute nodes too, + * else the later re-execution of + * transformConstraintAttrs will misbehave. + */ + case CONSTR_ATTR_DEFERRABLE: + case CONSTR_ATTR_NOT_DEFERRABLE: + case CONSTR_ATTR_DEFERRED: + case CONSTR_ATTR_IMMEDIATE: + case CONSTR_ATTR_ENFORCED: + case CONSTR_ATTR_NOT_ENFORCED: + if (afterFK) + entryconstraints = + foreach_delete_current(entryconstraints, + colconstr); + break; + + default: + /* Any following constraint attributes are unrelated */ + afterFK = false; + break; + } + } + + /* Now make a modified ColumnDef to put into newElts */ + newentry = makeNode(ColumnDef); + memcpy(newentry, entry, sizeof(ColumnDef)); + newentry->constraints = entryconstraints; + newElts = lappend(newElts, newentry); + } + else + { + /* Other node types pass through unchanged */ + newElts = lappend(newElts, element); + } + } + + newstmt->tableElts = newElts; + return newstmt; +} + /* * transformPartitionCmd * Analyze the ATTACH/DETACH/SPLIT PARTITION command diff --git a/src/test/modules/test_ddl_deparse/expected/create_schema.out b/src/test/modules/test_ddl_deparse/expected/create_schema.out index 8ab4eb03385..6ed85ef7446 100644 --- a/src/test/modules/test_ddl_deparse/expected/create_schema.out +++ b/src/test/modules/test_ddl_deparse/expected/create_schema.out @@ -17,3 +17,28 @@ CREATE SCHEMA element_test NOTICE: DDL test: type simple, tag CREATE SCHEMA NOTICE: DDL test: type simple, tag CREATE TABLE NOTICE: DDL test: type simple, tag CREATE VIEW +CREATE SCHEMA regress_schema_1 +CREATE TABLE t4( + b INT, + a INT REFERENCES t5 DEFERRABLE INITIALLY DEFERRED NOT ENFORCED + REFERENCES t6 DEFERRABLE INITIALLY DEFERRED, + CONSTRAINT fk FOREIGN KEY (a) REFERENCES t6 DEFERRABLE) +CREATE TABLE t5 (a INT, b INT, PRIMARY KEY (a)) +CREATE TABLE t6 (a INT, b INT, PRIMARY KEY (a)); +NOTICE: DDL test: type simple, tag CREATE SCHEMA +NOTICE: DDL test: type simple, tag CREATE TABLE +NOTICE: DDL test: type simple, tag CREATE TABLE +NOTICE: DDL test: type simple, tag CREATE INDEX +NOTICE: DDL test: type simple, tag CREATE TABLE +NOTICE: DDL test: type simple, tag CREATE INDEX +NOTICE: DDL test: type alter table, tag ALTER TABLE +NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint t4_a_fkey on table regress_schema_1.t4 +NOTICE: DDL test: type alter table, tag ALTER TABLE +NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint t4_a_fkey1 on table regress_schema_1.t4 +NOTICE: DDL test: type alter table, tag ALTER TABLE +NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint fk on table regress_schema_1.t4 +DROP SCHEMA regress_schema_1 CASCADE; +NOTICE: drop cascades to 3 other objects +DETAIL: drop cascades to table regress_schema_1.t4 +drop cascades to table regress_schema_1.t5 +drop cascades to table regress_schema_1.t6 diff --git a/src/test/modules/test_ddl_deparse/sql/create_schema.sql b/src/test/modules/test_ddl_deparse/sql/create_schema.sql index f314dc2b840..145aef2a75a 100644 --- a/src/test/modules/test_ddl_deparse/sql/create_schema.sql +++ b/src/test/modules/test_ddl_deparse/sql/create_schema.sql @@ -15,3 +15,14 @@ CREATE SCHEMA IF NOT EXISTS baz; CREATE SCHEMA element_test CREATE TABLE foo (id int) CREATE VIEW bar AS SELECT * FROM foo; + +CREATE SCHEMA regress_schema_1 +CREATE TABLE t4( + b INT, + a INT REFERENCES t5 DEFERRABLE INITIALLY DEFERRED NOT ENFORCED + REFERENCES t6 DEFERRABLE INITIALLY DEFERRED, + CONSTRAINT fk FOREIGN KEY (a) REFERENCES t6 DEFERRABLE) +CREATE TABLE t5 (a INT, b INT, PRIMARY KEY (a)) +CREATE TABLE t6 (a INT, b INT, PRIMARY KEY (a)); + +DROP SCHEMA regress_schema_1 CASCADE; diff --git a/src/test/regress/expected/create_schema.out b/src/test/regress/expected/create_schema.out index 4ab947a60a8..b34b9988962 100644 --- a/src/test/regress/expected/create_schema.out +++ b/src/test/regress/expected/create_schema.out @@ -131,5 +131,51 @@ CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE DROP SCHEMA regress_schema_1 CASCADE; NOTICE: drop cascades to table regress_schema_1.tab RESET ROLE; +-- Test forward-referencing foreign key clauses. +CREATE SCHEMA regress_schema_fk + CREATE TABLE regress_schema_fk.t2 ( + b int, + a int REFERENCES t1 DEFERRABLE INITIALLY DEFERRED NOT ENFORCED + REFERENCES t3 DEFERRABLE INITIALLY DEFERRED, + CONSTRAINT fk FOREIGN KEY (a) REFERENCES t1 NOT DEFERRABLE) + CREATE TABLE regress_schema_fk.t1 (a int PRIMARY KEY) + CREATE TABLE t3 (a int PRIMARY KEY) + CREATE TABLE t4 ( + b int, + a int REFERENCES t5 NOT DEFERRABLE ENFORCED + REFERENCES t6 DEFERRABLE INITIALLY IMMEDIATE, + CONSTRAINT fk FOREIGN KEY (a) REFERENCES t6 DEFERRABLE INITIALLY DEFERRED) + CREATE TABLE t5 (a int, b int, PRIMARY KEY (a)) + CREATE TABLE t6 (a int, b int, PRIMARY KEY (a)); +\d regress_schema_fk.t2 + Table "regress_schema_fk.t2" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + b | integer | | | + a | integer | | | +Foreign-key constraints: + "fk" FOREIGN KEY (a) REFERENCES regress_schema_fk.t1(a) + "t2_a_fkey" FOREIGN KEY (a) REFERENCES regress_schema_fk.t1(a) DEFERRABLE INITIALLY DEFERRED NOT ENFORCED + "t2_a_fkey1" FOREIGN KEY (a) REFERENCES regress_schema_fk.t3(a) DEFERRABLE INITIALLY DEFERRED + +\d regress_schema_fk.t4 + Table "regress_schema_fk.t4" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+--------- + b | integer | | | + a | integer | | | +Foreign-key constraints: + "fk" FOREIGN KEY (a) REFERENCES regress_schema_fk.t6(a) DEFERRABLE INITIALLY DEFERRED + "t4_a_fkey" FOREIGN KEY (a) REFERENCES regress_schema_fk.t5(a) + "t4_a_fkey1" FOREIGN KEY (a) REFERENCES regress_schema_fk.t6(a) DEFERRABLE + +DROP SCHEMA regress_schema_fk CASCADE; +NOTICE: drop cascades to 6 other objects +DETAIL: drop cascades to table regress_schema_fk.t2 +drop cascades to table regress_schema_fk.t1 +drop cascades to table regress_schema_fk.t3 +drop cascades to table regress_schema_fk.t4 +drop cascades to table regress_schema_fk.t5 +drop cascades to table regress_schema_fk.t6 -- Clean up DROP ROLE regress_create_schema_role; diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out index 4c32e1dcaf5..065f586310f 100644 --- a/src/test/regress/expected/event_trigger.out +++ b/src/test/regress/expected/event_trigger.out @@ -427,11 +427,11 @@ NOTICE: END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.one_col_ 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 VIEW type=view identity=evttrig.one_view +NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.two -- View with column additions CREATE OR REPLACE VIEW evttrig.one_view AS SELECT * FROM evttrig.two, evttrig.id; NOTICE: END: command_tag=CREATE VIEW type=view identity=evttrig.one_view diff --git a/src/test/regress/sql/create_schema.sql b/src/test/regress/sql/create_schema.sql index 62651342114..0f2accc59ec 100644 --- a/src/test/regress/sql/create_schema.sql +++ b/src/test/regress/sql/create_schema.sql @@ -71,5 +71,32 @@ CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE DROP SCHEMA regress_schema_1 CASCADE; RESET ROLE; +-- Test forward-referencing foreign key clauses. +CREATE SCHEMA regress_schema_fk + CREATE TABLE regress_schema_fk.t2 ( + b int, + a int REFERENCES t1 DEFERRABLE INITIALLY DEFERRED NOT ENFORCED + REFERENCES t3 DEFERRABLE INITIALLY DEFERRED, + CONSTRAINT fk FOREIGN KEY (a) REFERENCES t1 NOT DEFERRABLE) + + CREATE TABLE regress_schema_fk.t1 (a int PRIMARY KEY) + + CREATE TABLE t3 (a int PRIMARY KEY) + + CREATE TABLE t4 ( + b int, + a int REFERENCES t5 NOT DEFERRABLE ENFORCED + REFERENCES t6 DEFERRABLE INITIALLY IMMEDIATE, + CONSTRAINT fk FOREIGN KEY (a) REFERENCES t6 DEFERRABLE INITIALLY DEFERRED) + + CREATE TABLE t5 (a int, b int, PRIMARY KEY (a)) + + CREATE TABLE t6 (a int, b int, PRIMARY KEY (a)); + +\d regress_schema_fk.t2 +\d regress_schema_fk.t4 + +DROP SCHEMA regress_schema_fk CASCADE; + -- Clean up DROP ROLE regress_create_schema_role;