aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/parser/parse_utilcmd.c27
-rw-r--r--src/backend/tcop/utility.c28
-rw-r--r--src/test/regress/expected/create_table_like.out16
-rw-r--r--src/test/regress/sql/create_table_like.sql5
4 files changed, 56 insertions, 20 deletions
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 254c0f65c2b..c709abad2b0 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -86,6 +86,7 @@ typedef struct
List *ckconstraints; /* CHECK constraints */
List *fkconstraints; /* FOREIGN KEY constraints */
List *ixconstraints; /* index-creating constraints */
+ List *likeclauses; /* LIKE clauses that need post-processing */
List *extstats; /* cloned extended statistics */
List *blist; /* "before list" of things to do before
* creating the table */
@@ -243,6 +244,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
+ cxt.likeclauses = NIL;
cxt.extstats = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
@@ -310,6 +312,20 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
transformIndexConstraints(&cxt);
/*
+ * Re-consideration of LIKE clauses should happen after creation of
+ * indexes, but before creation of foreign keys. This order is critical
+ * because a LIKE clause may attempt to create a primary key. If there's
+ * also a pkey in the main CREATE TABLE list, creation of that will not
+ * check for a duplicate at runtime (since index_check_primary_key()
+ * expects that we rejected dups here). Creation of the LIKE-generated
+ * pkey behaves like ALTER TABLE ADD, so it will check, but obviously that
+ * only works if it happens second. On the other hand, we want to make
+ * pkeys before foreign key constraints, in case the user tries to make a
+ * self-referential FK.
+ */
+ cxt.alist = list_concat(cxt.alist, cxt.likeclauses);
+
+ /*
* Postprocess foreign-key constraints.
*/
transformFKConstraints(&cxt, true, false);
@@ -923,7 +939,7 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
* Change the LIKE <srctable> portion of a CREATE TABLE statement into
* column definitions that recreate the user defined column portions of
* <srctable>. Also, if there are any LIKE options that we can't fully
- * process at this point, add the TableLikeClause to cxt->alist, which
+ * process at this point, add the TableLikeClause to cxt->likeclauses, which
* will cause utility.c to call expandTableLikeClause() after the new
* table has been created.
*/
@@ -1088,15 +1104,15 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
* We cannot yet deal with defaults, CHECK constraints, or indexes, since
* we don't yet know what column numbers the copied columns will have in
* the finished table. If any of those options are specified, add the
- * LIKE clause to cxt->alist so that expandTableLikeClause will be called
- * after we do know that.
+ * LIKE clause to cxt->likeclauses so that expandTableLikeClause will be
+ * called after we do know that.
*/
if (table_like_clause->options &
(CREATE_TABLE_LIKE_DEFAULTS |
CREATE_TABLE_LIKE_GENERATED |
CREATE_TABLE_LIKE_CONSTRAINTS |
CREATE_TABLE_LIKE_INDEXES))
- cxt->alist = lappend(cxt->alist, table_like_clause);
+ cxt->likeclauses = lappend(cxt->likeclauses, table_like_clause);
/*
* We may copy extended statistics if requested, since the representation
@@ -2692,7 +2708,7 @@ transformFKConstraints(CreateStmtContext *cxt,
* Note: the ADD CONSTRAINT command must also execute after any index
* creation commands. Thus, this should run after
* transformIndexConstraints, so that the CREATE INDEX commands are
- * already in cxt->alist.
+ * already in cxt->alist. See also the handling of cxt->likeclauses.
*/
if (!isAddConstraint)
{
@@ -3205,6 +3221,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.ckconstraints = NIL;
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
+ cxt.likeclauses = NIL;
cxt.extstats = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index f398027fa61..81ac9b1cb2d 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -1138,17 +1138,22 @@ ProcessUtilitySlow(ParseState *pstate,
case T_CreateForeignTableStmt:
{
List *stmts;
- ListCell *l;
RangeVar *table_rv = NULL;
/* Run parse analysis ... */
stmts = transformCreateStmt((CreateStmt *) parsetree,
queryString);
- /* ... and do it */
- foreach(l, stmts)
+ /*
+ * ... and do it. We can't use foreach() because we may
+ * modify the list midway through, so pick off the
+ * elements one at a time, the hard way.
+ */
+ while (stmts != NIL)
{
- Node *stmt = (Node *) lfirst(l);
+ Node *stmt = (Node *) linitial(stmts);
+
+ stmts = list_delete_first(stmts);
if (IsA(stmt, CreateStmt))
{
@@ -1214,8 +1219,8 @@ ProcessUtilitySlow(ParseState *pstate,
/*
* Do delayed processing of LIKE options. This
* will result in additional sub-statements for us
- * to process. We can just tack those onto the
- * to-do list.
+ * to process. Those should get done before any
+ * remaining actions, so prepend them to "stmts".
*/
TableLikeClause *like = (TableLikeClause *) stmt;
List *morestmts;
@@ -1223,14 +1228,7 @@ ProcessUtilitySlow(ParseState *pstate,
Assert(table_rv != NULL);
morestmts = expandTableLikeClause(table_rv, like);
- stmts = list_concat(stmts, morestmts);
-
- /*
- * We don't need a CCI now, besides which the "l"
- * list pointer is now possibly invalid, so just
- * skip the CCI test below.
- */
- continue;
+ stmts = list_concat(morestmts, stmts);
}
else
{
@@ -1258,7 +1256,7 @@ ProcessUtilitySlow(ParseState *pstate,
}
/* Need CCI between commands */
- if (lnext(stmts, l) != NULL)
+ if (stmts != NIL)
CommandCounterIncrement();
}
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 912c73d351e..01e3a8435da 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -283,6 +283,22 @@ ERROR: duplicate key value violates unique constraint "inhg_x_key"
DETAIL: Key (x)=(15) already exists.
DROP TABLE inhg;
DROP TABLE inhz;
+/* Use primary key imported by LIKE for self-referential FK constraint */
+CREATE TABLE inhz (x text REFERENCES inhz, LIKE inhx INCLUDING INDEXES);
+\d inhz
+ Table "public.inhz"
+ Column | Type | Collation | Nullable | Default
+--------+------+-----------+----------+---------
+ x | text | | |
+ xx | text | | not null |
+Indexes:
+ "inhz_pkey" PRIMARY KEY, btree (xx)
+Foreign-key constraints:
+ "inhz_x_fkey" FOREIGN KEY (x) REFERENCES inhz(xx)
+Referenced by:
+ TABLE "inhz" CONSTRAINT "inhz_x_fkey" FOREIGN KEY (x) REFERENCES inhz(xx)
+
+DROP TABLE inhz;
-- including storage and comments
CREATE TABLE ctlt1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text);
CREATE INDEX ctlt1_b_key ON ctlt1 (b);
diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql
index e484bac0a46..63548b3d11d 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -114,6 +114,11 @@ INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail
DROP TABLE inhg;
DROP TABLE inhz;
+/* Use primary key imported by LIKE for self-referential FK constraint */
+CREATE TABLE inhz (x text REFERENCES inhz, LIKE inhx INCLUDING INDEXES);
+\d inhz
+DROP TABLE inhz;
+
-- including storage and comments
CREATE TABLE ctlt1 (a text CHECK (length(a) > 2) PRIMARY KEY, b text);
CREATE INDEX ctlt1_b_key ON ctlt1 (b);