From ca569103615d440426f628c7c3d94b2e7869020f Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Sun, 5 Apr 2020 13:16:55 +0200 Subject: [PATCH] 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 --- src/backend/parser/parse_utilcmd.c | 21 ++++++++++++-- .../regress/expected/create_table_like.out | 29 ++++++++++--------- src/test/regress/sql/create_table_like.sql | 19 ++++++------ 3 files changed, 43 insertions(+), 26 deletions(-) diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 484ca40ddf..b0daad69d5 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -914,6 +914,7 @@ static void transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause) { AttrNumber parent_attno; + AttrNumber new_attno; Relation relation; TupleDesc tupleDesc; TupleConstr *constr; @@ -976,6 +977,22 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla */ attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * tupleDesc->natts); + new_attno = 1; + for (parent_attno = 1; parent_attno <= tupleDesc->natts; + parent_attno++) + { + Form_pg_attribute attribute = TupleDescAttr(tupleDesc, + parent_attno - 1); + + /* + * Ignore dropped columns in the parent. attmap entry is left zero. + */ + if (attribute->attisdropped) + continue; + + attmap[parent_attno - 1] = list_length(cxt->columns) + (new_attno++); + } + /* * Insert the copied attributes into the cxt for the new table definition. */ @@ -988,7 +1005,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla ColumnDef *def; /* - * Ignore dropped columns in the parent. attmap entry is left zero. + * Ignore dropped columns in the parent. */ if (attribute->attisdropped) continue; @@ -1020,8 +1037,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla */ cxt->columns = lappend(cxt->columns, def); - attmap[parent_attno - 1] = list_length(cxt->columns); - /* * Copy default, if present and it should be copied. We have separate * options for plain default expressions and GENERATED defaults. diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out index 2a063a8369..da0e8ac298 100644 --- a/src/test/regress/expected/create_table_like.out +++ b/src/test/regress/expected/create_table_like.out @@ -159,14 +159,15 @@ SELECT * FROM test_like_gen_3; (1 row) DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3; -CREATE TABLE test_like_4 (a int, b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED); +-- also test generated column with a "forward" reference (bug #16342) +CREATE TABLE test_like_4 (b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED, a int); \d test_like_4 Table "public.test_like_4" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+------------------------------------ - a | integer | | | b | integer | | | 42 c | integer | | | generated always as (a * 2) stored + a | integer | | | CREATE TABLE test_like_4a (LIKE test_like_4); 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 Table "public.test_like_4a" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- - a | integer | | | b | integer | | | c | integer | | | + a | integer | | | -INSERT INTO test_like_4a VALUES(11); -TABLE test_like_4a; +INSERT INTO test_like_4a (a) VALUES(11); +SELECT a, b, c FROM test_like_4a; a | b | c ----+---+--- 11 | | @@ -191,12 +192,12 @@ TABLE test_like_4a; Table "public.test_like_4b" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- - a | integer | | | b | integer | | | 42 c | integer | | | + a | integer | | | -INSERT INTO test_like_4b VALUES(11); -TABLE test_like_4b; +INSERT INTO test_like_4b (a) VALUES(11); +SELECT a, b, c FROM test_like_4b; a | b | c ----+----+--- 11 | 42 | @@ -206,12 +207,12 @@ TABLE test_like_4b; Table "public.test_like_4c" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+------------------------------------ - a | integer | | | b | integer | | | c | integer | | | generated always as (a * 2) stored + a | integer | | | -INSERT INTO test_like_4c VALUES(11); -TABLE test_like_4c; +INSERT INTO test_like_4c (a) VALUES(11); +SELECT a, b, c FROM test_like_4c; a | b | c ----+---+---- 11 | | 22 @@ -221,12 +222,12 @@ TABLE test_like_4c; Table "public.test_like_4d" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+------------------------------------ - a | integer | | | b | integer | | | 42 c | integer | | | generated always as (a * 2) stored + a | integer | | | -INSERT INTO test_like_4d VALUES(11); -TABLE test_like_4d; +INSERT INTO test_like_4d (a) VALUES(11); +SELECT a, b, c FROM test_like_4d; a | b | c ----+----+---- 11 | 42 | 22 diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql index 589ee12ebc..5d30f63e00 100644 --- a/src/test/regress/sql/create_table_like.sql +++ b/src/test/regress/sql/create_table_like.sql @@ -65,24 +65,25 @@ CREATE TABLE test_like_gen_3 (LIKE test_like_gen_1 INCLUDING GENERATED); SELECT * FROM test_like_gen_3; DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3; -CREATE TABLE test_like_4 (a int, b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED); +-- also test generated column with a "forward" reference (bug #16342) +CREATE TABLE test_like_4 (b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED, a int); \d test_like_4 CREATE TABLE test_like_4a (LIKE test_like_4); CREATE TABLE test_like_4b (LIKE test_like_4 INCLUDING DEFAULTS); CREATE TABLE test_like_4c (LIKE test_like_4 INCLUDING GENERATED); CREATE TABLE test_like_4d (LIKE test_like_4 INCLUDING DEFAULTS INCLUDING GENERATED); \d test_like_4a -INSERT INTO test_like_4a VALUES(11); -TABLE test_like_4a; +INSERT INTO test_like_4a (a) VALUES(11); +SELECT a, b, c FROM test_like_4a; \d test_like_4b -INSERT INTO test_like_4b VALUES(11); -TABLE test_like_4b; +INSERT INTO test_like_4b (a) VALUES(11); +SELECT a, b, c FROM test_like_4b; \d test_like_4c -INSERT INTO test_like_4c VALUES(11); -TABLE test_like_4c; +INSERT INTO test_like_4c (a) VALUES(11); +SELECT a, b, c FROM test_like_4c; \d test_like_4d -INSERT INTO test_like_4d VALUES(11); -TABLE test_like_4d; +INSERT INTO test_like_4d (a) VALUES(11); +SELECT a, b, c FROM test_like_4d; DROP TABLE test_like_4, test_like_4a, test_like_4b, test_like_4c, test_like_4d; CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */ -- 2.26.0