Skip to content

Commit e92e4a2

Browse files
committed
Fix CREATE TABLE LIKE INCLUDING GENERATED column order issue
CREATE TABLE LIKE INCLUDING GENERATED would fail if a generated column referred to a column with a higher attribute number. This is because the column mapping mechanism created the mapping incrementally as columns are added. This was sufficient for previous uses of that mechanism (omitting dropped columns), and it also happened to work if generated columns only referred to columns with lower attribute numbers, but here it failed. This fix is to build the attribute mapping in a separate loop before processing the columns in detail. Bug: #16342 Reported-by: Ethan Waldo <ewaldo@healthetechs.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
1 parent c4f82a7 commit e92e4a2

File tree

3 files changed

+47
-26
lines changed

3 files changed

+47
-26
lines changed

src/backend/parser/parse_utilcmd.c

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,7 @@ static void
924924
transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
925925
{
926926
AttrNumber parent_attno;
927+
AttrNumber new_attno;
927928
Relation relation;
928929
TupleDesc tupleDesc;
929930
TupleConstr *constr;
@@ -986,6 +987,26 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
986987
*/
987988
attmap = make_attrmap(tupleDesc->natts);
988989

990+
/*
991+
* We must fill the attmap now so that it can be used to process generated
992+
* column default expressions in the per-column loop below.
993+
*/
994+
new_attno = 1;
995+
for (parent_attno = 1; parent_attno <= tupleDesc->natts;
996+
parent_attno++)
997+
{
998+
Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
999+
parent_attno - 1);
1000+
1001+
/*
1002+
* Ignore dropped columns in the parent. attmap entry is left zero.
1003+
*/
1004+
if (attribute->attisdropped)
1005+
continue;
1006+
1007+
attmap->attnums[parent_attno - 1] = list_length(cxt->columns) + (new_attno++);
1008+
}
1009+
9891010
/*
9901011
* Insert the copied attributes into the cxt for the new table definition.
9911012
*/
@@ -998,7 +1019,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
9981019
ColumnDef *def;
9991020

10001021
/*
1001-
* Ignore dropped columns in the parent. attmap entry is left zero.
1022+
* Ignore dropped columns in the parent.
10021023
*/
10031024
if (attribute->attisdropped)
10041025
continue;
@@ -1030,8 +1051,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
10301051
*/
10311052
cxt->columns = lappend(cxt->columns, def);
10321053

1033-
attmap->attnums[parent_attno - 1] = list_length(cxt->columns);
1034-
10351054
/*
10361055
* Copy default, if present and it should be copied. We have separate
10371056
* options for plain default expressions and GENERATED defaults.

src/test/regress/expected/create_table_like.out

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -159,14 +159,15 @@ SELECT * FROM test_like_gen_3;
159159
(1 row)
160160

161161
DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3;
162-
CREATE TABLE test_like_4 (a int, b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED);
162+
-- also test generated column with a "forward" reference (bug #16342)
163+
CREATE TABLE test_like_4 (b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED, a int);
163164
\d test_like_4
164165
Table "public.test_like_4"
165166
Column | Type | Collation | Nullable | Default
166167
--------+---------+-----------+----------+------------------------------------
167-
a | integer | | |
168168
b | integer | | | 42
169169
c | integer | | | generated always as (a * 2) stored
170+
a | integer | | |
170171

171172
CREATE TABLE test_like_4a (LIKE test_like_4);
172173
CREATE TABLE test_like_4b (LIKE test_like_4 INCLUDING DEFAULTS);
@@ -176,12 +177,12 @@ CREATE TABLE test_like_4d (LIKE test_like_4 INCLUDING DEFAULTS INCLUDING GENERAT
176177
Table "public.test_like_4a"
177178
Column | Type | Collation | Nullable | Default
178179
--------+---------+-----------+----------+---------
179-
a | integer | | |
180180
b | integer | | |
181181
c | integer | | |
182+
a | integer | | |
182183

183-
INSERT INTO test_like_4a VALUES(11);
184-
TABLE test_like_4a;
184+
INSERT INTO test_like_4a (a) VALUES(11);
185+
SELECT a, b, c FROM test_like_4a;
185186
a | b | c
186187
----+---+---
187188
11 | |
@@ -191,12 +192,12 @@ TABLE test_like_4a;
191192
Table "public.test_like_4b"
192193
Column | Type | Collation | Nullable | Default
193194
--------+---------+-----------+----------+---------
194-
a | integer | | |
195195
b | integer | | | 42
196196
c | integer | | |
197+
a | integer | | |
197198

198-
INSERT INTO test_like_4b VALUES(11);
199-
TABLE test_like_4b;
199+
INSERT INTO test_like_4b (a) VALUES(11);
200+
SELECT a, b, c FROM test_like_4b;
200201
a | b | c
201202
----+----+---
202203
11 | 42 |
@@ -206,12 +207,12 @@ TABLE test_like_4b;
206207
Table "public.test_like_4c"
207208
Column | Type | Collation | Nullable | Default
208209
--------+---------+-----------+----------+------------------------------------
209-
a | integer | | |
210210
b | integer | | |
211211
c | integer | | | generated always as (a * 2) stored
212+
a | integer | | |
212213

213-
INSERT INTO test_like_4c VALUES(11);
214-
TABLE test_like_4c;
214+
INSERT INTO test_like_4c (a) VALUES(11);
215+
SELECT a, b, c FROM test_like_4c;
215216
a | b | c
216217
----+---+----
217218
11 | | 22
@@ -221,12 +222,12 @@ TABLE test_like_4c;
221222
Table "public.test_like_4d"
222223
Column | Type | Collation | Nullable | Default
223224
--------+---------+-----------+----------+------------------------------------
224-
a | integer | | |
225225
b | integer | | | 42
226226
c | integer | | | generated always as (a * 2) stored
227+
a | integer | | |
227228

228-
INSERT INTO test_like_4d VALUES(11);
229-
TABLE test_like_4d;
229+
INSERT INTO test_like_4d (a) VALUES(11);
230+
SELECT a, b, c FROM test_like_4d;
230231
a | b | c
231232
----+----+----
232233
11 | 42 | 22

src/test/regress/sql/create_table_like.sql

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,24 +65,25 @@ INSERT INTO test_like_gen_3 (a) VALUES (1);
6565
SELECT * FROM test_like_gen_3;
6666
DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3;
6767

68-
CREATE TABLE test_like_4 (a int, b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED);
68+
-- also test generated column with a "forward" reference (bug #16342)
69+
CREATE TABLE test_like_4 (b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED, a int);
6970
\d test_like_4
7071
CREATE TABLE test_like_4a (LIKE test_like_4);
7172
CREATE TABLE test_like_4b (LIKE test_like_4 INCLUDING DEFAULTS);
7273
CREATE TABLE test_like_4c (LIKE test_like_4 INCLUDING GENERATED);
7374
CREATE TABLE test_like_4d (LIKE test_like_4 INCLUDING DEFAULTS INCLUDING GENERATED);
7475
\d test_like_4a
75-
INSERT INTO test_like_4a VALUES(11);
76-
TABLE test_like_4a;
76+
INSERT INTO test_like_4a (a) VALUES(11);
77+
SELECT a, b, c FROM test_like_4a;
7778
\d test_like_4b
78-
INSERT INTO test_like_4b VALUES(11);
79-
TABLE test_like_4b;
79+
INSERT INTO test_like_4b (a) VALUES(11);
80+
SELECT a, b, c FROM test_like_4b;
8081
\d test_like_4c
81-
INSERT INTO test_like_4c VALUES(11);
82-
TABLE test_like_4c;
82+
INSERT INTO test_like_4c (a) VALUES(11);
83+
SELECT a, b, c FROM test_like_4c;
8384
\d test_like_4d
84-
INSERT INTO test_like_4d VALUES(11);
85-
TABLE test_like_4d;
85+
INSERT INTO test_like_4d (a) VALUES(11);
86+
SELECT a, b, c FROM test_like_4d;
8687
DROP TABLE test_like_4, test_like_4a, test_like_4b, test_like_4c, test_like_4d;
8788

8889
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */

0 commit comments

Comments
 (0)