Fix check for child column generation status matching parent.
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 16 Feb 2023 23:51:55 +0000 (18:51 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 16 Feb 2023 23:51:55 +0000 (18:51 -0500)
In commit 8bf6ec3ba, I mistakenly supposed that MergeAttributes'
loop over saved_schema was reprocessing column definitions that
had already been checked earlier: there is a variant syntax for
creating a child partition in which that's not true.  So we need
to duplicate the full check appearing further up.

(Actually, I believe that the "if (restdef->identity)" part is
not reachable, because we reject identity on partitions earlier.
But it seems wise to keep the check, in case that's ever relaxed,
and to keep this code in sync with the other instance.)

Per report from Alexander Lakhin.

Discussion: https://postgr.es/m/4a8200ca-8378-653e-38ed-b2e1f1611aa6@gmail.com

src/backend/commands/tablecmds.c
src/test/regress/expected/generated.out
src/test/regress/sql/generated.sql

index 1293545947e80a214f4463df88f74b11b8ec822e..62d9917ca36ee57e24c67d118d144fbb20454b2f 100644 (file)
@@ -3015,17 +3015,34 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
                                        coldef->is_not_null |= restdef->is_not_null;
 
                                        /*
-                                        * As above, reject generated columns in partitions that
-                                        * are not generated in the parent.
+                                        * Check for conflicts related to generated columns.
+                                        *
+                                        * Same rules as above: generated-ness has to match the
+                                        * parent, but the contents of the generation expression
+                                        * can be different.
                                         */
-                                       if (restdef->generated && !coldef->generated)
-                                               ereport(ERROR,
-                                                               (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
-                                                                errmsg("child column \"%s\" specifies generation expression",
-                                                                               restdef->colname),
-                                                                errhint("A child table column cannot be generated unless its parent column is.")));
-                                       /* Other way around should have been dealt with above */
-                                       Assert(!(coldef->generated && !restdef->generated));
+                                       if (coldef->generated)
+                                       {
+                                               if (restdef->raw_default && !restdef->generated)
+                                                       ereport(ERROR,
+                                                                       (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
+                                                                        errmsg("column \"%s\" inherits from generated column but specifies default",
+                                                                                       restdef->colname)));
+                                               if (restdef->identity)
+                                                       ereport(ERROR,
+                                                                       (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
+                                                                        errmsg("column \"%s\" inherits from generated column but specifies identity",
+                                                                                       restdef->colname)));
+                                       }
+                                       else
+                                       {
+                                               if (restdef->generated)
+                                                       ereport(ERROR,
+                                                                       (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
+                                                                        errmsg("child column \"%s\" specifies generation expression",
+                                                                                       restdef->colname),
+                                                                        errhint("A child table column cannot be generated unless its parent column is.")));
+                                       }
 
                                        /*
                                         * Override the parent's default value for this column
index 11940c851f267fdd2ec3adce96a1ab84888369a3..702774d644439fe26e673288bd4f7d0fa610213a 100644 (file)
@@ -730,10 +730,26 @@ CREATE TABLE gtest_child PARTITION OF gtest_parent
 CREATE TABLE gtest_child2 PARTITION OF gtest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 22) STORED  -- overrides gen expr
 ) FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+CREATE TABLE gtest_child3 PARTITION OF gtest_parent (
+    f3 DEFAULT 42  -- error
+) FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+ERROR:  column "f3" inherits from generated column but specifies default
+CREATE TABLE gtest_child3 PARTITION OF gtest_parent (
+    f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY  -- error
+) FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+ERROR:  identity columns are not supported on partitions
 CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint);
 ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
 ERROR:  column "f3" in child table must be a generated column
 DROP TABLE gtest_child3;
+CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint DEFAULT 42);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
+ERROR:  column "f3" in child table must be a generated column
+DROP TABLE gtest_child3;
+CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS IDENTITY);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
+ERROR:  column "f3" in child table must be a generated column
+DROP TABLE gtest_child3;
 CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 33) STORED);
 ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
 \d gtest_child
index 87ec842116288cd0ffb06652f426d083533b685b..36f6bff348356c032f2348140f02aa95a8bba846 100644 (file)
@@ -390,9 +390,21 @@ CREATE TABLE gtest_child PARTITION OF gtest_parent
 CREATE TABLE gtest_child2 PARTITION OF gtest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS (f2 * 22) STORED  -- overrides gen expr
 ) FOR VALUES FROM ('2016-08-01') TO ('2016-09-01');
+CREATE TABLE gtest_child3 PARTITION OF gtest_parent (
+    f3 DEFAULT 42  -- error
+) FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
+CREATE TABLE gtest_child3 PARTITION OF gtest_parent (
+    f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY  -- error
+) FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
 CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint);
 ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
 DROP TABLE gtest_child3;
+CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint DEFAULT 42);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
+DROP TABLE gtest_child3;
+CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS IDENTITY);
+ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01'); -- error
+DROP TABLE gtest_child3;
 CREATE TABLE gtest_child3 (f1 date NOT NULL, f2 bigint, f3 bigint GENERATED ALWAYS AS (f2 * 33) STORED);
 ALTER TABLE gtest_parent ATTACH PARTITION gtest_child3 FOR VALUES FROM ('2016-09-01') TO ('2016-10-01');
 \d gtest_child