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
This commit is contained in:
parent
e48ce7ef0e
commit
64190d65f2
@ -13909,6 +13909,66 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
|
|||||||
errmsg("column \"%s\" in child table must be marked NOT NULL",
|
errmsg("column \"%s\" in child table must be marked NOT NULL",
|
||||||
attributeName)));
|
attributeName)));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If parent column is 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.
|
||||||
|
*
|
||||||
|
* The test we apply is to see whether they reverse-compile to the
|
||||||
|
* same source string. This insulates us from issues like whether
|
||||||
|
* attributes have the same physical column numbers in parent and
|
||||||
|
* child relations. (See also constraints_equivalent().)
|
||||||
|
*/
|
||||||
|
if (attribute->attgenerated && childatt->attgenerated)
|
||||||
|
{
|
||||||
|
TupleConstr *child_constr = child_rel->rd_att->constr;
|
||||||
|
TupleConstr *parent_constr = parent_rel->rd_att->constr;
|
||||||
|
char *child_expr = NULL;
|
||||||
|
char *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 =
|
||||||
|
TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
|
||||||
|
CStringGetTextDatum(child_constr->defval[i].adbin),
|
||||||
|
ObjectIdGetDatum(child_rel->rd_id)));
|
||||||
|
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 =
|
||||||
|
TextDatumGetCString(DirectFunctionCall2(pg_get_expr,
|
||||||
|
CStringGetTextDatum(parent_constr->defval[i].adbin),
|
||||||
|
ObjectIdGetDatum(parent_rel->rd_id)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Assert(parent_expr != NULL);
|
||||||
|
|
||||||
|
if (strcmp(child_expr, parent_expr) != 0)
|
||||||
|
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
|
* OK, bump the child column's inheritance count. (If we fail
|
||||||
* later on, this change will just roll back.)
|
* later on, this change will just roll back.)
|
||||||
|
@ -240,6 +240,17 @@ SELECT * FROM gtest_normal;
|
|||||||
2 | 4
|
2 | 4
|
||||||
(2 rows)
|
(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
|
-- 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 GENERATED ALWAYS AS (a * 22) STORED) INHERITS (gtest1); -- error
|
||||||
NOTICE: merging column "b" with inherited definition
|
NOTICE: merging column "b" with inherited definition
|
||||||
@ -251,6 +262,16 @@ ERROR: column "b" inherits from generated column but specifies default
|
|||||||
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error
|
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) INHERITS (gtest1); -- error
|
||||||
NOTICE: merging column "b" with inherited definition
|
NOTICE: merging column "b" with inherited definition
|
||||||
ERROR: column "b" inherits from generated column but specifies identity
|
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
|
||||||
|
CREATE TABLE gtestxx_4 (b int GENERATED ALWAYS AS (a * 2) STORED, a int NOT NULL);
|
||||||
|
ALTER TABLE gtestxx_4 INHERIT gtest1; -- ok
|
||||||
-- test multiple inheritance mismatches
|
-- test multiple inheritance mismatches
|
||||||
CREATE TABLE gtesty (x int, b int);
|
CREATE TABLE gtesty (x int, b int);
|
||||||
CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error
|
CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error
|
||||||
|
@ -96,11 +96,25 @@ INSERT INTO gtest_normal (a) VALUES (1);
|
|||||||
INSERT INTO gtest_normal_child (a) VALUES (2);
|
INSERT INTO gtest_normal_child (a) VALUES (2);
|
||||||
SELECT * FROM gtest_normal;
|
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
|
-- 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 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 DEFAULT 10) INHERITS (gtest1); -- error
|
||||||
CREATE TABLE gtestx (x int, b int GENERATED ALWAYS AS IDENTITY) 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
|
||||||
|
CREATE TABLE gtestxx_4 (b int GENERATED ALWAYS AS (a * 2) STORED, a int NOT NULL);
|
||||||
|
ALTER TABLE gtestxx_4 INHERIT gtest1; -- ok
|
||||||
|
|
||||||
-- test multiple inheritance mismatches
|
-- test multiple inheritance mismatches
|
||||||
CREATE TABLE gtesty (x int, b int);
|
CREATE TABLE gtesty (x int, b int);
|
||||||
CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error
|
CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error
|
||||||
|
Loading…
x
Reference in New Issue
Block a user