]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Ensure that expandTableLikeClause() re-examines the same table.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 1 Dec 2020 19:02:28 +0000 (14:02 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 1 Dec 2020 19:02:28 +0000 (14:02 -0500)
As it stood, expandTableLikeClause() re-did the same relation_openrv
call that transformTableLikeClause() had done.  However there are
scenarios where this would not find the same table as expected.
We hold lock on the LIKE source table, so it can't be renamed or
dropped, but another table could appear before it in the search path.
This explains the odd behavior reported in bug #16758 when cloning a
table as a temp table of the same name.  This case worked as expected
before commit 502898192 introduced the need to open the source table
twice, so we should fix it.

To make really sure we get the same table, let's re-open it by OID not
name.  That requires adding an OID field to struct TableLikeClause,
which is a little nervous-making from an ABI standpoint, but as long
as it's at the end I don't think there's any serious risk.

Per bug #16758 from Marc Boeren.  Like the previous patch,
back-patch to all supported branches.

Discussion: https://postgr.es/m/16758-840e84a6cfab276d@postgresql.org

src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/parser/gram.y
src/backend/parser/parse_utilcmd.c
src/include/nodes/parsenodes.h
src/test/regress/expected/create_table_like.out
src/test/regress/sql/create_table_like.sql

index c2b1ccf99f1e7606dd109c94a297f4f360e7b2fd..fcdb7d2cdb24cd75f124aa02211c933e702a8879 100644 (file)
@@ -3025,6 +3025,7 @@ _copyTableLikeClause(const TableLikeClause *from)
 
        COPY_NODE_FIELD(relation);
        COPY_SCALAR_FIELD(options);
+       COPY_SCALAR_FIELD(relationOid);
 
        return newnode;
 }
index a7e1694e23b62d0a23878be9b4e526046e506021..1d9475965a1ac113c9f60c47240c0f3b4aa9d7e0 100644 (file)
@@ -1172,6 +1172,7 @@ _equalTableLikeClause(const TableLikeClause *a, const TableLikeClause *b)
 {
        COMPARE_NODE_FIELD(relation);
        COMPARE_SCALAR_FIELD(options);
+       COMPARE_SCALAR_FIELD(relationOid);
 
        return true;
 }
index 96cefabb8f8796c88b4ddd68abd7ac36f191e701..b89251586bcd4acd24d3eb90d2effd80bf558533 100644 (file)
@@ -2541,6 +2541,7 @@ _outTableLikeClause(StringInfo str, const TableLikeClause *node)
 
        WRITE_NODE_FIELD(relation);
        WRITE_UINT_FIELD(options);
+       WRITE_OID_FIELD(relationOid);
 }
 
 static void
index 6e15df231fae06c6192985ad9157d746dfc7a474..31ec7d196f2152bc4847134613da0008098cce5a 100644 (file)
@@ -3174,6 +3174,7 @@ TableLikeClause:
                                        TableLikeClause *n = makeNode(TableLikeClause);
                                        n->relation = $2;
                                        n->options = $3;
+                                       n->relationOid = InvalidOid;
                                        $$ = (Node *)n;
                                }
                ;
index 57ea3fa1163affdbe80bfc5f2e4daeb72b92b29f..ebf9d19b874fec0c8d7f4455eef08667f30e38e7 100644 (file)
@@ -909,12 +909,16 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
         * 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->likeclauses so that expandTableLikeClause will be called
-        * after we do know that.
+        * after we do know that.  Also, remember the relation OID so that
+        * expandTableLikeClause is certain to open the same table.
         */
        if (table_like_clause->options &
                (CREATE_TABLE_LIKE_CONSTRAINTS |
                 CREATE_TABLE_LIKE_INDEXES))
+       {
+               table_like_clause->relationOid = RelationGetRelid(relation);
                cxt->likeclauses = lappend(cxt->likeclauses, table_like_clause);
+       }
 
        /*
         * Close the parent rel, but keep our AccessShareLock on it until xact
@@ -948,9 +952,13 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
         * Open the relation referenced by the LIKE clause.  We should still have
         * the table lock obtained by transformTableLikeClause (and this'll throw
         * an assertion failure if not).  Hence, no need to recheck privileges
-        * etc.
+        * etc.  We must open the rel by OID not name, to be sure we get the same
+        * table.
         */
-       relation = relation_openrv(table_like_clause->relation, NoLock);
+       if (!OidIsValid(table_like_clause->relationOid))
+               elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
+
+       relation = relation_open(table_like_clause->relationOid, NoLock);
 
        tupleDesc = RelationGetDescr(relation);
        constr = tupleDesc->constr;
index e6e11142a8412de4911a510ed517fa0a1478e169..4dd2d4ecfb799ce4cf100177f94fe35a2c7da9a8 100644 (file)
@@ -610,6 +610,7 @@ typedef struct TableLikeClause
        NodeTag         type;
        RangeVar   *relation;
        bits32          options;                /* OR of TableLikeOption flags */
+       Oid                     relationOid;    /* If table has been looked up, its OID */
 } TableLikeClause;
 
 typedef enum TableLikeOption
index 4739cd5ee3c61978affde7e7323e1ab594d0e5ca..734319ef0238c062568d3811099927a6ff2293cd 100644 (file)
@@ -275,6 +275,25 @@ Check constraints:
     "ctlt1_a_check" CHECK (length(a) > 2)
 
 DROP TABLE public.pg_attrdef;
+-- Check that LIKE isn't confused when new table masks the old, either
+BEGIN;
+CREATE SCHEMA ctl_schema;
+SET LOCAL search_path = ctl_schema, public;
+CREATE TABLE ctlt1 (LIKE ctlt1 INCLUDING ALL);
+\d+ ctlt1
+                     Table "ctl_schema.ctlt1"
+ Column | Type | Modifiers | Storage  | Stats target | Description 
+--------+------+-----------+----------+--------------+-------------
+ a      | text | not null  | main     |              | A
+ b      | text |           | extended |              | B
+Indexes:
+    "ctlt1_pkey" PRIMARY KEY, btree (a)
+    "ctlt1_b_idx" btree (b)
+    "ctlt1_expr_idx" btree ((a || b))
+Check constraints:
+    "ctlt1_a_check" CHECK (length(a) > 2)
+
+ROLLBACK;
 DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE;
 NOTICE:  drop cascades to table inhe
 /* LIKE with other relation kinds */
index 86fa43415ed3ab0d8710e12554c29902170eddb2..dd0b212661fe5cd7bf3293320013db871f9d7eb3 100644 (file)
@@ -122,6 +122,14 @@ CREATE TABLE pg_attrdef (LIKE ctlt1 INCLUDING ALL);
 \d+ public.pg_attrdef
 DROP TABLE public.pg_attrdef;
 
+-- Check that LIKE isn't confused when new table masks the old, either
+BEGIN;
+CREATE SCHEMA ctl_schema;
+SET LOCAL search_path = ctl_schema, public;
+CREATE TABLE ctlt1 (LIKE ctlt1 INCLUDING ALL);
+\d+ ctlt1
+ROLLBACK;
+
 DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE;