Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 13ff139

Browse files
committed
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. Discussion: https://www.postgresql.org/message-id/22de27f6-7096-8d96-4619-7b882932ca25@2ndquadrant.com
1 parent bd9e46a commit 13ff139

File tree

3 files changed

+95
-0
lines changed

3 files changed

+95
-0
lines changed

src/backend/commands/tablecmds.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13432,6 +13432,66 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
1343213432
errmsg("column \"%s\" in child table must be marked NOT NULL",
1343313433
attributeName)));
1343413434

13435+
/*
13436+
* If parent column is generated, child column must be, too.
13437+
*/
13438+
if (attribute->attgenerated && !childatt->attgenerated)
13439+
ereport(ERROR,
13440+
(errcode(ERRCODE_DATATYPE_MISMATCH),
13441+
errmsg("column \"%s\" in child table must be a generated column",
13442+
attributeName)));
13443+
13444+
/*
13445+
* Check that both generation expressions match.
13446+
*
13447+
* The test we apply is to see whether they reverse-compile to the
13448+
* same source string. This insulates us from issues like whether
13449+
* attributes have the same physical column numbers in parent and
13450+
* child relations. (See also constraints_equivalent().)
13451+
*/
13452+
if (attribute->attgenerated && childatt->attgenerated)
13453+
{
13454+
TupleConstr *child_constr = child_rel->rd_att->constr;
13455+
TupleConstr *parent_constr = parent_rel->rd_att->constr;
13456+
char *child_expr = NULL;
13457+
char *parent_expr = NULL;
13458+
13459+
Assert(child_constr != NULL);
13460+
Assert(parent_constr != NULL);
13461+
13462+
for (int i = 0; i < child_constr->num_defval; i++)
13463+
{
13464+
if (child_constr->defval[i].adnum == childatt->attnum)
13465+
{
13466+
child_expr =
13467+
TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
13468+
CStringGetTextDatum(child_constr->defval[i].adbin),
13469+
ObjectIdGetDatum(child_rel->rd_id)));
13470+
break;
13471+
}
13472+
}
13473+
Assert(child_expr != NULL);
13474+
13475+
for (int i = 0; i < parent_constr->num_defval; i++)
13476+
{
13477+
if (parent_constr->defval[i].adnum == attribute->attnum)
13478+
{
13479+
parent_expr =
13480+
TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
13481+
CStringGetTextDatum(parent_constr->defval[i].adbin),
13482+
ObjectIdGetDatum(parent_rel->rd_id)));
13483+
break;
13484+
}
13485+
}
13486+
Assert(parent_expr != NULL);
13487+
13488+
if (strcmp(child_expr, parent_expr) != 0)
13489+
ereport(ERROR,
13490+
(errcode(ERRCODE_DATATYPE_MISMATCH),
13491+
errmsg("column \"%s\" in child table has a conflicting generation expression",
13492+
attributeName)));
13493+
}
13494+
1343513495
/*
1343613496
* OK, bump the child column's inheritance count. (If we fail
1343713497
* later on, this change will just roll back.)

src/test/regress/expected/generated.out

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,17 @@ SELECT * FROM gtest_normal;
240240
2 | 4
241241
(2 rows)
242242

243+
CREATE TABLE gtest_normal_child2 (a int, b int GENERATED ALWAYS AS (a * 3) STORED);
244+
ALTER TABLE gtest_normal_child2 INHERIT gtest_normal;
245+
INSERT INTO gtest_normal_child2 (a) VALUES (3);
246+
SELECT * FROM gtest_normal;
247+
a | b
248+
---+---
249+
1 |
250+
2 | 4
251+
3 | 9
252+
(3 rows)
253+
243254
-- test inheritance mismatches between parent and child
244255
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- error
245256
NOTICE: merging column "b" with inherited definition
@@ -251,6 +262,16 @@ ERROR: column "b" inherits from generated column but specifies default
251262
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error
252263
NOTICE: merging column "b" with inherited definition
253264
ERROR: column "b" inherits from generated column but specifies identity
265+
CREATE TABLE gtestxx_1 (a int NOT NULL, b int);
266+
ALTER TABLE gtestxx_1 INHERIT gtest1; -- error
267+
ERROR: column "b" in child table must be a generated column
268+
CREATE TABLE gtestxx_2 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 22) STORED);
269+
ALTER TABLE gtestxx_2 INHERIT gtest1; -- error
270+
ERROR: column "b" in child table has a conflicting generation expression
271+
CREATE TABLE gtestxx_3 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 2) STORED);
272+
ALTER TABLE gtestxx_3 INHERIT gtest1; -- ok
273+
CREATE TABLE gtestxx_4 (b int GENERATED ALWAYS AS (a * 2) STORED, a int NOT NULL);
274+
ALTER TABLE gtestxx_4 INHERIT gtest1; -- ok
254275
-- test multiple inheritance mismatches
255276
CREATE TABLE gtesty (x int, b int);
256277
CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error

src/test/regress/sql/generated.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,25 @@ INSERT INTO gtest_normal (a) VALUES (1);
9696
INSERT INTO gtest_normal_child (a) VALUES (2);
9797
SELECT * FROM gtest_normal;
9898

99+
CREATE TABLE gtest_normal_child2 (a int, b int GENERATED ALWAYS AS (a * 3) STORED);
100+
ALTER TABLE gtest_normal_child2 INHERIT gtest_normal;
101+
INSERT INTO gtest_normal_child2 (a) VALUES (3);
102+
SELECT * FROM gtest_normal;
103+
99104
-- test inheritance mismatches between parent and child
100105
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- error
101106
CREATE TABLE gtestx (x int, b int DEFAULT 10) INHERITS (gtest1); -- error
102107
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error
103108

109+
CREATE TABLE gtestxx_1 (a int NOT NULL, b int);
110+
ALTER TABLE gtestxx_1 INHERIT gtest1; -- error
111+
CREATE TABLE gtestxx_2 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 22) STORED);
112+
ALTER TABLE gtestxx_2 INHERIT gtest1; -- error
113+
CREATE TABLE gtestxx_3 (a int NOT NULL, b int GENERATED ALWAYS AS (a * 2) STORED);
114+
ALTER TABLE gtestxx_3 INHERIT gtest1; -- ok
115+
CREATE TABLE gtestxx_4 (b int GENERATED ALWAYS AS (a * 2) STORED, a int NOT NULL);
116+
ALTER TABLE gtestxx_4 INHERIT gtest1; -- ok
117+
104118
-- test multiple inheritance mismatches
105119
CREATE TABLE gtesty (x int, b int);
106120
CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error

0 commit comments

Comments
 (0)