diff --git a/doc/src/sgml/ddl.sgml b/doc/src/sgml/ddl.sgml index a20e5fb366..bf9f0181ea 100644 --- a/doc/src/sgml/ddl.sgml +++ b/doc/src/sgml/ddl.sgml @@ -324,6 +324,32 @@ CREATE TABLE people ( linkend="sql-createforeigntable"/> for details. + + For inheritance: + + + + If a parent column is a generated column, a child column must also be + a generated column using the same expression. In the definition of + the child column, leave off the GENERATED clause, + as it will be copied from the parent. + + + + + In case of multiple inheritance, if one parent column is a generated + column, then all parent columns must be generated columns and with the + same expression. + + + + + If a parent column is not a generated column, a child column may be + defined to be a generated column or not. + + + + diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index b36683e749..2a15e07ede 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -2577,12 +2577,55 @@ MergeAttributes(List *schema, List *supers, char relpersistence, def->is_local = true; /* Merge of NOT NULL constraints = OR 'em together */ def->is_not_null |= newdef->is_not_null; + + /* + * Check for conflicts related to generated columns. + * + * If the parent column is generated, the child column must be + * unadorned and will be made a generated column. (We could + * in theory allow the child column definition specifying the + * exact same generation expression, but that's a bit + * complicated to implement and doesn't seem very useful.) We + * also check that the child column doesn't specify a default + * value or identity, which matches the rules for a single + * column in parse_util.c. + */ + if (def->generated) + { + if (newdef->generated) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_DEFINITION), + errmsg("child column \"%s\" specifies generation expression", + def->colname), + errhint("Omit the generation expression in the definition of the child table column to inherit the generation expression from the parent table."))); + if (newdef->raw_default && !newdef->generated) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_DEFINITION), + errmsg("column \"%s\" inherits from generated column but specifies default", + def->colname))); + if (newdef->identity) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_DEFINITION), + errmsg("column \"%s\" inherits from generated column but specifies identity", + def->colname))); + } + /* + * If the parent column is not generated, then take whatever + * the child column definition says. + */ + else + { + if (newdef->generated) + def->generated = newdef->generated; + } + /* If new def has a default, override previous default */ if (newdef->raw_default != NULL) { def->raw_default = newdef->raw_default; def->cooked_default = newdef->cooked_default; } + } else { @@ -2668,11 +2711,19 @@ MergeAttributes(List *schema, List *supers, char relpersistence, ColumnDef *def = lfirst(entry); if (def->cooked_default == &bogus_marker) - ereport(ERROR, - (errcode(ERRCODE_INVALID_COLUMN_DEFINITION), - errmsg("column \"%s\" inherits conflicting default values", - def->colname), - errhint("To resolve the conflict, specify a default explicitly."))); + { + if (def->generated) + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_DEFINITION), + errmsg("column \"%s\" inherits conflicting generation expressions", + def->colname))); + else + ereport(ERROR, + (errcode(ERRCODE_INVALID_COLUMN_DEFINITION), + errmsg("column \"%s\" inherits conflicting default values", + def->colname), + errhint("To resolve the conflict, specify a default explicitly."))); + } } } diff --git a/src/test/regress/expected/generated.out b/src/test/regress/expected/generated.out index f87c86a8ce..cea278a89a 100644 --- a/src/test/regress/expected/generated.out +++ b/src/test/regress/expected/generated.out @@ -219,12 +219,54 @@ SELECT * FROM gtest1; 4 | 8 (2 rows) --- test inheritance mismatch +CREATE TABLE gtest_normal (a int, b int); +CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED) INHERITS (gtest_normal); +NOTICE: merging column "a" with inherited definition +NOTICE: merging column "b" with inherited definition +\d gtest_normal_child + Table "public.gtest_normal_child" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+------------------------------------ + a | integer | | | + b | integer | | | generated always as (a * 2) stored +Inherits: gtest_normal + +INSERT INTO gtest_normal (a) VALUES (1); +INSERT INTO gtest_normal_child (a) VALUES (2); +SELECT * FROM gtest_normal; + a | b +---+--- + 1 | + 2 | 4 +(2 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 +ERROR: child column "b" specifies generation expression +HINT: Omit the generation expression in the definition of the child table column to inherit the generation expression from the parent table. +CREATE TABLE gtestx (x int, b int DEFAULT 10) INHERITS (gtest1); -- error +NOTICE: merging column "b" with inherited definition +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 +-- test multiple inheritance mismatches CREATE TABLE gtesty (x int, b int); CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error NOTICE: merging multiple inherited definitions of column "b" ERROR: inherited column "b" has a generation conflict DROP TABLE gtesty; +CREATE TABLE gtesty (x int, b int GENERATED ALWAYS AS (x * 22) STORED); +CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error +NOTICE: merging multiple inherited definitions of column "b" +ERROR: column "b" inherits conflicting generation expressions +DROP TABLE gtesty; +CREATE TABLE gtesty (x int, b int DEFAULT 55); +CREATE TABLE gtest1_2 () INHERITS (gtest0, gtesty); -- error +NOTICE: merging multiple inherited definitions of column "b" +ERROR: inherited column "b" has a generation conflict +DROP TABLE gtesty; -- test stored update CREATE TABLE gtest3 (a int, b int GENERATED ALWAYS AS (a * 3) STORED); INSERT INTO gtest3 (a) VALUES (1), (2), (3), (NULL); diff --git a/src/test/regress/sql/generated.sql b/src/test/regress/sql/generated.sql index bdcedbb991..f089f9a74d 100644 --- a/src/test/regress/sql/generated.sql +++ b/src/test/regress/sql/generated.sql @@ -89,11 +89,31 @@ INSERT INTO gtest1_1 VALUES (4); SELECT * FROM gtest1_1; SELECT * FROM gtest1; --- test inheritance mismatch +CREATE TABLE gtest_normal (a int, b int); +CREATE TABLE gtest_normal_child (a int, b int GENERATED ALWAYS AS (a * 2) STORED) INHERITS (gtest_normal); +\d gtest_normal_child +INSERT INTO gtest_normal (a) VALUES (1); +INSERT INTO gtest_normal_child (a) VALUES (2); +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 + +-- test multiple inheritance mismatches CREATE TABLE gtesty (x int, b int); CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error DROP TABLE gtesty; +CREATE TABLE gtesty (x int, b int GENERATED ALWAYS AS (x * 22) STORED); +CREATE TABLE gtest1_2 () INHERITS (gtest1, gtesty); -- error +DROP TABLE gtesty; + +CREATE TABLE gtesty (x int, b int DEFAULT 55); +CREATE TABLE gtest1_2 () INHERITS (gtest0, gtesty); -- error +DROP TABLE gtesty; + -- test stored update CREATE TABLE gtest3 (a int, b int GENERATED ALWAYS AS (a * 3) STORED); INSERT INTO gtest3 (a) VALUES (1), (2), (3), (NULL);