Forbid marking an identity column as nullable.
authorTom Lane <[email protected]>
Fri, 12 Mar 2021 16:08:42 +0000 (11:08 -0500)
committerTom Lane <[email protected]>
Fri, 12 Mar 2021 16:08:42 +0000 (11:08 -0500)
GENERATED ALWAYS AS IDENTITY implies NOT NULL, but the code failed
to complain if you overrode that with "GENERATED ALWAYS AS IDENTITY
NULL".  One might think the old behavior was a feature, but it was
inconsistent because the outcome varied depending on the order of
the clauses, so it seems to have been just an oversight.

Per bug #16913 from Pavel Boev.  Back-patch to v10 where identity
columns were introduced.

Vik Fearing (minor tweaks by me)

Discussion: https://postgr.es/m/16913-3b5198410f67d8c6@postgresql.org

doc/src/sgml/ref/create_table.sgml
src/backend/parser/parse_utilcmd.c
src/test/regress/expected/identity.out
src/test/regress/sql/identity.sql

index 7d00d22c64629a47ebaa405201d7179ef574f3f4..f12e76c2efcca506235e20ad41f843e2336b4e5f 100644 (file)
@@ -689,6 +689,7 @@ FROM ( { <replaceable class="PARAMETER">numeric_literal</replaceable> | <replace
       column</firstterm>.  It will have an implicit sequence attached to it
       and the column in new rows will automatically have values from the
       sequence assigned to it.
+      Such a column is implicitly <literal>NOT NULL</literal>.
      </para>
 
      <para>
index 6e1093a858d250bd772aadb75ce06595efac6c83..7cf5944ab377392dfb45072ee7cb516f925898d8 100644 (file)
@@ -728,7 +728,17 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
 
                    column->identity = constraint->generated_when;
                    saw_identity = true;
-                   column->is_not_null = TRUE;
+
+                   /* An identity column is implicitly NOT NULL */
+                   if (saw_nullable && !column->is_not_null)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                errmsg("conflicting NULL/NOT NULL declarations for column \"%s\" of table \"%s\"",
+                                       column->colname, cxt->relation->relname),
+                                parser_errposition(cxt->pstate,
+                                                   constraint->location)));
+                   column->is_not_null = true;
+                   saw_nullable = true;
                    break;
                }
 
index 3562cd4f68a1f061c1fe0412d65d2206ae3f98d5..374807cc94fcb30c97d819d86f4c6fae6e280d7d 100644 (file)
@@ -386,3 +386,16 @@ CREATE TABLE itest_child PARTITION OF itest_parent (
 ) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
 ERROR:  identity columns are not supported on partitions
 DROP TABLE itest_parent;
+-- Identity columns must be NOT NULL (cf bug #16913)
+CREATE TABLE itest15 (id integer GENERATED ALWAYS AS IDENTITY NULL); -- fail
+ERROR:  conflicting NULL/NOT NULL declarations for column "id" of table "itest15"
+LINE 1: ...ABLE itest15 (id integer GENERATED ALWAYS AS IDENTITY NULL);
+                                                                 ^
+CREATE TABLE itest15 (id integer NULL GENERATED ALWAYS AS IDENTITY); -- fail
+ERROR:  conflicting NULL/NOT NULL declarations for column "id" of table "itest15"
+LINE 1: CREATE TABLE itest15 (id integer NULL GENERATED ALWAYS AS ID...
+                                              ^
+CREATE TABLE itest15 (id integer GENERATED ALWAYS AS IDENTITY NOT NULL);
+DROP TABLE itest15;
+CREATE TABLE itest15 (id integer NOT NULL GENERATED ALWAYS AS IDENTITY);
+DROP TABLE itest15;
index e3459be497abe484aef015a136ab8d22eb1e0407..d71a2a4d498d9f0e6a096523418fb979f2d4edae 100644 (file)
@@ -246,3 +246,12 @@ CREATE TABLE itest_child PARTITION OF itest_parent (
     f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
 ) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
 DROP TABLE itest_parent;
+
+-- Identity columns must be NOT NULL (cf bug #16913)
+
+CREATE TABLE itest15 (id integer GENERATED ALWAYS AS IDENTITY NULL); -- fail
+CREATE TABLE itest15 (id integer NULL GENERATED ALWAYS AS IDENTITY); -- fail
+CREATE TABLE itest15 (id integer GENERATED ALWAYS AS IDENTITY NOT NULL);
+DROP TABLE itest15;
+CREATE TABLE itest15 (id integer NOT NULL GENERATED ALWAYS AS IDENTITY);
+DROP TABLE itest15;