From bd6ee462e5ad4a6514940e7e78d40c4f28f3dc3f Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Wed, 3 Feb 2021 20:14:05 +0100 Subject: [PATCH] WIP: Fix ALTER TABLE / INHERIT with generated columns When running ALTER TABLE t2 INHERIT t1, we must check that columns in t2 that correspond to a generated column in t1 are also generated and have the same generation expression. Otherwise, this would allow creating setups that a normal CREATE TABLE sequence would not allow. --- src/backend/commands/tablecmds.c | 49 +++++++++++++++++++++++++ src/test/regress/expected/generated.out | 19 ++++++++++ src/test/regress/sql/generated.sql | 13 +++++++ 3 files changed, 81 insertions(+) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 420991e315..3d4fb4ce7e 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -13963,6 +13963,55 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel) errmsg("column \"%s\" in child table must be marked NOT NULL", attributeName))); + /* + * If parent column generated, child column must be, too. + */ + if (attribute->attgenerated && !childatt->attgenerated) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("column \"%s\" in child table must be a generated column", + attributeName))); + + /* + * Check that both generation expressions match. + */ + if (attribute->attgenerated && childatt->attgenerated) + { + TupleConstr *child_constr = child_rel->rd_att->constr; + TupleConstr *parent_constr = parent_rel->rd_att->constr; + Node *child_expr = NULL; + Node *parent_expr = NULL; + + Assert(child_constr != NULL); + Assert(parent_constr != NULL); + + for (int i = 0; i < child_constr->num_defval; i++) + { + if (child_constr->defval[i].adnum == childatt->attnum) + { + child_expr = stringToNode(child_constr->defval[i].adbin); + break; + } + } + Assert(child_expr != NULL); + + for (int i = 0; i < parent_constr->num_defval; i++) + { + if (parent_constr->defval[i].adnum == attribute->attnum) + { + parent_expr = stringToNode(parent_constr->defval[i].adbin); + break; + } + } + Assert(parent_expr != NULL); + + if (!equal(child_expr, parent_expr)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("column \"%s\" in child table has a conflicting generation expression", + attributeName))); + } + /* * OK, bump the child column's inheritance count. (If we fail * later on, this change will just roll back.) diff --git a/src/test/regress/expected/generated.out b/src/test/regress/expected/generated.out index ca721d38bf..f2ed3864a1 100644 --- a/src/test/regress/expected/generated.out +++ b/src/test/regress/expected/generated.out @@ -281,6 +281,17 @@ SELECT * FROM gtest_normal; 2 | 4 (2 rows) +CREATE TABLE gtest_normal_child2 (a int, b int GENERATED ALWAYS AS (a * 3) STORED); +ALTER TABLE gtest_normal_child2 INHERIT gtest_normal; +INSERT INTO gtest_normal_child2 (a) VALUES (3); +SELECT * FROM gtest_normal; + a | b +---+--- + 1 | + 2 | 4 + 3 | 9 +(3 rows) + -- test inheritance mismatches between parent and child CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- error NOTICE: merging column "b" with inherited definition @@ -292,6 +303,14 @@ ERROR: column "b" inherits from generated column but specifies default CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error NOTICE: merging column "b" with inherited definition ERROR: column "b" inherits from generated column but specifies identity +CREATE TABLE gtestxx_1 (a int NOT NULL, b int); +ALTER TABLE gtestxx_1 INHERIT gtest1; -- error +ERROR: column "b" in child table must be a generated column +CREATE TABLE gtestxx_2 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 22) STORED); +ALTER TABLE gtestxx_2 INHERIT gtest1; -- error +ERROR: column "b" in child table has a conflicting generation expression +CREATE TABLE gtestxx_3 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 2) STORED); +ALTER TABLE gtestxx_3 INHERIT gtest1; -- ok -- test multiple inheritance mismatches CREATE TABLE gtesty (x int, b int); CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error diff --git a/src/test/regress/sql/generated.sql b/src/test/regress/sql/generated.sql index bd2b0bfaaa..e30dfe649d 100644 --- a/src/test/regress/sql/generated.sql +++ b/src/test/regress/sql/generated.sql @@ -113,11 +113,24 @@ CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED INSERT INTO gtest_normal_child (a) VALUES (2); SELECT * FROM gtest_normal; +CREATE TABLE gtest_normal_child2 (a int, b int GENERATED ALWAYS AS (a * 3) STORED); +ALTER TABLE gtest_normal_child2 INHERIT gtest_normal; +INSERT INTO gtest_normal_child2 (a) VALUES (3); +SELECT * FROM gtest_normal; + -- test inheritance mismatches between parent and child CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- error CREATE TABLE gtestx (x int, b int DEFAULT 10) INHERITS (gtest1); -- error CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error +CREATE TABLE gtestxx_1 (a int NOT NULL, b int); +ALTER TABLE gtestxx_1 INHERIT gtest1; -- error +CREATE TABLE gtestxx_2 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 22) STORED); +ALTER TABLE gtestxx_2 INHERIT gtest1; -- error +CREATE TABLE gtestxx_3 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 2) STORED); +ALTER TABLE gtestxx_3 INHERIT gtest1; -- ok + + -- test multiple inheritance mismatches CREATE TABLE gtesty (x int, b int); CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error -- 2.30.0