Skip to content

Commit f00c440

Browse files
committed
Ensure that expandTableLikeClause() re-examines the same table.
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 5028981 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
1 parent d0bbe21 commit f00c440

File tree

8 files changed

+43
-3
lines changed

8 files changed

+43
-3
lines changed

src/backend/nodes/copyfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3025,6 +3025,7 @@ _copyTableLikeClause(const TableLikeClause *from)
30253025

30263026
COPY_NODE_FIELD(relation);
30273027
COPY_SCALAR_FIELD(options);
3028+
COPY_SCALAR_FIELD(relationOid);
30283029

30293030
return newnode;
30303031
}

src/backend/nodes/equalfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,7 @@ _equalTableLikeClause(const TableLikeClause *a, const TableLikeClause *b)
11721172
{
11731173
COMPARE_NODE_FIELD(relation);
11741174
COMPARE_SCALAR_FIELD(options);
1175+
COMPARE_SCALAR_FIELD(relationOid);
11751176

11761177
return true;
11771178
}

src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,6 +2541,7 @@ _outTableLikeClause(StringInfo str, const TableLikeClause *node)
25412541

25422542
WRITE_NODE_FIELD(relation);
25432543
WRITE_UINT_FIELD(options);
2544+
WRITE_OID_FIELD(relationOid);
25442545
}
25452546

25462547
static void

src/backend/parser/gram.y

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3174,6 +3174,7 @@ TableLikeClause:
31743174
TableLikeClause *n = makeNode(TableLikeClause);
31753175
n->relation = $2;
31763176
n->options = $3;
3177+
n->relationOid = InvalidOid;
31773178
$$ = (Node *)n;
31783179
}
31793180
;

src/backend/parser/parse_utilcmd.c

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -909,12 +909,16 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
909909
* yet know what column numbers the copied columns will have in the
910910
* finished table. If any of those options are specified, add the LIKE
911911
* clause to cxt->likeclauses so that expandTableLikeClause will be called
912-
* after we do know that.
912+
* after we do know that. Also, remember the relation OID so that
913+
* expandTableLikeClause is certain to open the same table.
913914
*/
914915
if (table_like_clause->options &
915916
(CREATE_TABLE_LIKE_CONSTRAINTS |
916917
CREATE_TABLE_LIKE_INDEXES))
918+
{
919+
table_like_clause->relationOid = RelationGetRelid(relation);
917920
cxt->likeclauses = lappend(cxt->likeclauses, table_like_clause);
921+
}
918922

919923
/*
920924
* Close the parent rel, but keep our AccessShareLock on it until xact
@@ -948,9 +952,13 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
948952
* Open the relation referenced by the LIKE clause. We should still have
949953
* the table lock obtained by transformTableLikeClause (and this'll throw
950954
* an assertion failure if not). Hence, no need to recheck privileges
951-
* etc.
955+
* etc. We must open the rel by OID not name, to be sure we get the same
956+
* table.
952957
*/
953-
relation = relation_openrv(table_like_clause->relation, NoLock);
958+
if (!OidIsValid(table_like_clause->relationOid))
959+
elog(ERROR, "expandTableLikeClause called on untransformed LIKE clause");
960+
961+
relation = relation_open(table_like_clause->relationOid, NoLock);
954962

955963
tupleDesc = RelationGetDescr(relation);
956964
constr = tupleDesc->constr;

src/include/nodes/parsenodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ typedef struct TableLikeClause
610610
NodeTag type;
611611
RangeVar *relation;
612612
bits32 options; /* OR of TableLikeOption flags */
613+
Oid relationOid; /* If table has been looked up, its OID */
613614
} TableLikeClause;
614615

615616
typedef enum TableLikeOption

src/test/regress/expected/create_table_like.out

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,25 @@ Check constraints:
275275
"ctlt1_a_check" CHECK (length(a) > 2)
276276

277277
DROP TABLE public.pg_attrdef;
278+
-- Check that LIKE isn't confused when new table masks the old, either
279+
BEGIN;
280+
CREATE SCHEMA ctl_schema;
281+
SET LOCAL search_path = ctl_schema, public;
282+
CREATE TABLE ctlt1 (LIKE ctlt1 INCLUDING ALL);
283+
\d+ ctlt1
284+
Table "ctl_schema.ctlt1"
285+
Column | Type | Modifiers | Storage | Stats target | Description
286+
--------+------+-----------+----------+--------------+-------------
287+
a | text | not null | main | | A
288+
b | text | | extended | | B
289+
Indexes:
290+
"ctlt1_pkey" PRIMARY KEY, btree (a)
291+
"ctlt1_b_idx" btree (b)
292+
"ctlt1_expr_idx" btree ((a || b))
293+
Check constraints:
294+
"ctlt1_a_check" CHECK (length(a) > 2)
295+
296+
ROLLBACK;
278297
DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE;
279298
NOTICE: drop cascades to table inhe
280299
/* LIKE with other relation kinds */

src/test/regress/sql/create_table_like.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,14 @@ CREATE TABLE pg_attrdef (LIKE ctlt1 INCLUDING ALL);
122122
\d+ public.pg_attrdef
123123
DROP TABLE public.pg_attrdef;
124124

125+
-- Check that LIKE isn't confused when new table masks the old, either
126+
BEGIN;
127+
CREATE SCHEMA ctl_schema;
128+
SET LOCAL search_path = ctl_schema, public;
129+
CREATE TABLE ctlt1 (LIKE ctlt1 INCLUDING ALL);
130+
\d+ ctlt1
131+
ROLLBACK;
132+
125133
DROP TABLE ctlt1, ctlt2, ctlt3, ctlt4, ctlt12_storage, ctlt12_comments, ctlt1_inh, ctlt13_inh, ctlt13_like, ctlt_all, ctla, ctlb CASCADE;
126134

127135

0 commit comments

Comments
 (0)