summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2021-03-12 11:08:42 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2021-03-12 11:08:42 -0500
commite5794cd593940e48710693c0cdd97c6d7b91f500 (patch)
treed0c7cf1465b370ce7212d2d3ec6d3caefb8dd9c6
parentd2be6cdc55ad0eb0fa019a453491e1410c5315e8 (diff)
Forbid marking an identity column as nullable.
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
-rw-r--r--doc/src/sgml/ref/create_table.sgml1
-rw-r--r--src/backend/parser/parse_utilcmd.c12
-rw-r--r--src/test/regress/expected/identity.out13
-rw-r--r--src/test/regress/sql/identity.sql9
4 files changed, 34 insertions, 1 deletions
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 7d00d22c646..f12e76c2efc 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -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>
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 6e1093a858d..7cf5944ab37 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -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;
}
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 3562cd4f68a..374807cc94f 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -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;
diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql
index e3459be497a..d71a2a4d498 100644
--- a/src/test/regress/sql/identity.sql
+++ b/src/test/regress/sql/identity.sql
@@ -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;