aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorAlvaro Herrera <alvherre@alvh.no-ip.org>2023-08-25 13:31:24 +0200
committerAlvaro Herrera <alvherre@alvh.no-ip.org>2023-08-25 13:31:24 +0200
commitb0e96f311985bceba79825214f8e43f65afa653a (patch)
tree862e344e9a51ef034f3039d44c289a3c26fc656f /src/test
parent9c13b6814ac7943036c64b377675184b243f04e8 (diff)
downloadpostgresql-b0e96f311985bceba79825214f8e43f65afa653a.tar.gz
postgresql-b0e96f311985bceba79825214f8e43f65afa653a.zip
Catalog not-null constraints
We now create contype='n' pg_constraint rows for not-null constraints. We propagate these constraints to other tables during operations such as adding inheritance relationships, creating and attaching partitions and creating tables LIKE other tables. We also spawn not-null constraints for inheritance child tables when their parents have primary keys. These related constraints mostly follow the well-known rules of conislocal and coninhcount that we have for CHECK constraints, with some adaptations: for example, as opposed to CHECK constraints, we don't match not-null ones by name when descending a hierarchy to alter it, instead matching by column name that they apply to. This means we don't require the constraint names to be identical across a hierarchy. For now, we omit them for system catalogs. Maybe this is worth reconsidering. We don't support NOT VALID nor DEFERRABLE clauses either; these can be added as separate features later (this patch is already large and complicated enough.) psql shows these constraints in \d+. pg_dump requires some ad-hoc hacks, particularly when dumping a primary key. We now create one "throwaway" not-null constraint for each column in the PK together with the CREATE TABLE command, and once the PK is created, all those throwaway constraints are removed. This avoids having to check each tuple for nullness when the dump restores the primary key creation. pg_upgrading from an older release requires a somewhat brittle procedure to create a constraint state that matches what would be created if the database were being created fresh in Postgres 17. I have tested all the scenarios I could think of, and it works correctly as far as I can tell, but I could have neglected weird cases. This patch has been very long in the making. The first patch was written by Bernd Helmle in 2010 to add a new pg_constraint.contype value ('n'), which I (Álvaro) then hijacked in 2011 and 2012, until that one was killed by the realization that we ought to use contype='c' instead: manufactured CHECK constraints. However, later SQL standard development, as well as nonobvious emergent properties of that design (mostly, failure to distinguish them from "normal" CHECK constraints as well as the performance implication of having to test the CHECK expression) led us to reconsider this choice, so now the current implementation uses contype='n' again. During Postgres 16 this had already been introduced by commit e056c557aef4, but there were some problems mainly with the pg_upgrade procedure that couldn't be fixed in reasonable time, so it was reverted. In 2016 Vitaly Burovoy also worked on this feature[1] but found no consensus for his proposed approach, which was claimed to be closer to the letter of the standard, requiring an additional pg_attribute column to track the OID of the not-null constraint for that column. [1] https://postgr.es/m/CAKOSWNkN6HSyatuys8xZxzRCR-KL1OkHS5-b9qd9bf1Rad3PLA@mail.gmail.com Author: Álvaro Herrera <alvherre@alvh.no-ip.org> Author: Bernd Helmle <mailings@oopsware.de> Reviewed-by: Justin Pryzby <pryzby@telsasoft.com> Reviewed-by: Peter Eisentraut <peter.eisentraut@enterprisedb.com> Reviewed-by: Dean Rasheed <dean.a.rasheed@gmail.com>
Diffstat (limited to 'src/test')
-rw-r--r--src/test/modules/test_ddl_deparse/expected/alter_table.out18
-rw-r--r--src/test/modules/test_ddl_deparse/expected/create_table.out26
-rw-r--r--src/test/modules/test_ddl_deparse/test_ddl_deparse.c6
-rw-r--r--src/test/regress/expected/alter_table.out62
-rw-r--r--src/test/regress/expected/cluster.out7
-rw-r--r--src/test/regress/expected/constraints.out252
-rw-r--r--src/test/regress/expected/create_table.out41
-rw-r--r--src/test/regress/expected/create_table_like.out10
-rw-r--r--src/test/regress/expected/event_trigger.out2
-rw-r--r--src/test/regress/expected/foreign_data.out108
-rw-r--r--src/test/regress/expected/foreign_key.out16
-rw-r--r--src/test/regress/expected/generated.out2
-rw-r--r--src/test/regress/expected/identity.out4
-rw-r--r--src/test/regress/expected/indexing.out41
-rw-r--r--src/test/regress/expected/inherit.out442
-rw-r--r--src/test/regress/expected/publication.out6
-rw-r--r--src/test/regress/expected/replica_identity.out24
-rw-r--r--src/test/regress/expected/rowsecurity.out2
-rw-r--r--src/test/regress/sql/alter_table.sql24
-rw-r--r--src/test/regress/sql/constraints.sql102
-rw-r--r--src/test/regress/sql/create_table.sql6
-rw-r--r--src/test/regress/sql/indexing.sql8
-rw-r--r--src/test/regress/sql/inherit.sql183
-rw-r--r--src/test/regress/sql/replica_identity.sql15
24 files changed, 1319 insertions, 88 deletions
diff --git a/src/test/modules/test_ddl_deparse/expected/alter_table.out b/src/test/modules/test_ddl_deparse/expected/alter_table.out
index 87a1ab7aabc..ecde9d7422d 100644
--- a/src/test/modules/test_ddl_deparse/expected/alter_table.out
+++ b/src/test/modules/test_ddl_deparse/expected/alter_table.out
@@ -28,6 +28,7 @@ ALTER TABLE parent ADD COLUMN b serial;
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type alter table, tag ALTER TABLE
NOTICE: subcommand: type ADD COLUMN (and recurse) desc column b of table parent
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint parent_b_not_null on table parent
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
ALTER TABLE parent RENAME COLUMN b TO c;
NOTICE: DDL test: type simple, tag ALTER TABLE
@@ -57,24 +58,18 @@ NOTICE: subcommand: type DETACH PARTITION desc table part2
DROP TABLE part2;
ALTER TABLE part ADD PRIMARY KEY (a);
NOTICE: DDL test: type alter table, tag ALTER TABLE
-NOTICE: subcommand: type SET NOT NULL desc column a of table part
-NOTICE: subcommand: type SET NOT NULL desc column a of table part1
+NOTICE: subcommand: type SET ATTNOTNULL desc column a of table part
+NOTICE: subcommand: type SET ATTNOTNULL desc column a of table part1
NOTICE: subcommand: type ADD INDEX desc index part_pkey
ALTER TABLE parent ALTER COLUMN a SET NOT NULL;
NOTICE: DDL test: type alter table, tag ALTER TABLE
-NOTICE: subcommand: type SET NOT NULL desc column a of table parent
-NOTICE: subcommand: type SET NOT NULL desc column a of table child
-NOTICE: subcommand: type SET NOT NULL desc column a of table grandchild
+NOTICE: subcommand: type SET NOT NULL (and recurse) desc constraint parent_a_not_null on table parent
ALTER TABLE parent ALTER COLUMN a DROP NOT NULL;
NOTICE: DDL test: type alter table, tag ALTER TABLE
-NOTICE: subcommand: type DROP NOT NULL desc column a of table parent
-NOTICE: subcommand: type DROP NOT NULL desc column a of table child
-NOTICE: subcommand: type DROP NOT NULL desc column a of table grandchild
+NOTICE: subcommand: type DROP NOT NULL (and recurse) desc column a of table parent
ALTER TABLE parent ALTER COLUMN a SET NOT NULL;
NOTICE: DDL test: type alter table, tag ALTER TABLE
-NOTICE: subcommand: type SET NOT NULL desc column a of table parent
-NOTICE: subcommand: type SET NOT NULL desc column a of table child
-NOTICE: subcommand: type SET NOT NULL desc column a of table grandchild
+NOTICE: subcommand: type SET NOT NULL (and recurse) desc constraint parent_a_not_null on table parent
ALTER TABLE parent ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
@@ -116,6 +111,7 @@ NOTICE: DDL test: type alter table, tag ALTER TABLE
NOTICE: subcommand: type ALTER COLUMN SET TYPE desc column c of table parent
NOTICE: subcommand: type ALTER COLUMN SET TYPE desc column c of table child
NOTICE: subcommand: type ALTER COLUMN SET TYPE desc column c of table grandchild
+NOTICE: subcommand: type (re) ADD CONSTRAINT desc constraint parent_b_not_null on table parent
NOTICE: subcommand: type (re) ADD STATS desc statistics object parent_stat
ALTER TABLE parent ALTER COLUMN c SET DEFAULT 0;
NOTICE: DDL test: type alter table, tag ALTER TABLE
diff --git a/src/test/modules/test_ddl_deparse/expected/create_table.out b/src/test/modules/test_ddl_deparse/expected/create_table.out
index 2178ce83e9d..75b62aff4d5 100644
--- a/src/test/modules/test_ddl_deparse/expected/create_table.out
+++ b/src/test/modules/test_ddl_deparse/expected/create_table.out
@@ -54,6 +54,8 @@ NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type simple, tag CREATE SEQUENCE
NOTICE: DDL test: type simple, tag CREATE TABLE
+NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type SET ATTNOTNULL desc <NULL>
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type simple, tag ALTER SEQUENCE
@@ -74,6 +76,8 @@ CREATE TABLE IF NOT EXISTS fkey_table (
EXCLUDE USING btree (check_col_2 WITH =)
);
NOTICE: DDL test: type simple, tag CREATE TABLE
+NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type SET ATTNOTNULL desc <NULL>
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type alter table, tag ALTER TABLE
@@ -86,7 +90,7 @@ CREATE TABLE employees OF employee_type (
);
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type alter table, tag ALTER TABLE
-NOTICE: subcommand: type SET NOT NULL desc column name of table employees
+NOTICE: subcommand: type SET ATTNOTNULL desc column name of table employees
NOTICE: DDL test: type simple, tag CREATE INDEX
-- Inheritance
CREATE TABLE person (
@@ -96,6 +100,8 @@ CREATE TABLE person (
location point
);
NOTICE: DDL test: type simple, tag CREATE TABLE
+NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type SET ATTNOTNULL desc <NULL>
NOTICE: DDL test: type simple, tag CREATE INDEX
CREATE TABLE emp (
salary int4,
@@ -128,6 +134,10 @@ CREATE TABLE like_datatype_table (
EXCLUDING ALL
);
NOTICE: DDL test: type simple, tag CREATE TABLE
+NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint datatype_table_id_big_not_null on table like_datatype_table
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint datatype_table_id_not_null on table like_datatype_table
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint datatype_table_is_small_not_null on table like_datatype_table
CREATE TABLE like_fkey_table (
LIKE fkey_table
INCLUDING DEFAULTS
@@ -136,7 +146,13 @@ CREATE TABLE like_fkey_table (
);
NOTICE: DDL test: type simple, tag CREATE TABLE
NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type SET ATTNOTNULL desc column id of table like_fkey_table
NOTICE: subcommand: type ALTER COLUMN SET DEFAULT (precooked) desc column id of table like_fkey_table
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint fkey_table_big_id_not_null on table like_fkey_table
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint fkey_table_check_col_1_not_null on table like_fkey_table
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint fkey_table_check_col_2_not_null on table like_fkey_table
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint fkey_table_datatype_id_not_null on table like_fkey_table
+NOTICE: subcommand: type ADD CONSTRAINT (and recurse) desc constraint fkey_table_id_not_null on table like_fkey_table
NOTICE: DDL test: type simple, tag CREATE INDEX
NOTICE: DDL test: type simple, tag CREATE INDEX
-- Volatile table types
@@ -144,21 +160,29 @@ CREATE UNLOGGED TABLE unlogged_table (
id INT PRIMARY KEY
);
NOTICE: DDL test: type simple, tag CREATE TABLE
+NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type SET ATTNOTNULL desc <NULL>
NOTICE: DDL test: type simple, tag CREATE INDEX
CREATE TEMP TABLE temp_table (
id INT PRIMARY KEY
);
NOTICE: DDL test: type simple, tag CREATE TABLE
+NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type SET ATTNOTNULL desc <NULL>
NOTICE: DDL test: type simple, tag CREATE INDEX
CREATE TEMP TABLE temp_table_commit_delete (
id INT PRIMARY KEY
)
ON COMMIT DELETE ROWS;
NOTICE: DDL test: type simple, tag CREATE TABLE
+NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type SET ATTNOTNULL desc <NULL>
NOTICE: DDL test: type simple, tag CREATE INDEX
CREATE TEMP TABLE temp_table_commit_drop (
id INT PRIMARY KEY
)
ON COMMIT DROP;
NOTICE: DDL test: type simple, tag CREATE TABLE
+NOTICE: DDL test: type alter table, tag ALTER TABLE
+NOTICE: subcommand: type SET ATTNOTNULL desc <NULL>
NOTICE: DDL test: type simple, tag CREATE INDEX
diff --git a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
index 82f937fca4f..0302f79bb71 100644
--- a/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
+++ b/src/test/modules/test_ddl_deparse/test_ddl_deparse.c
@@ -129,12 +129,12 @@ get_altertable_subcmdinfo(PG_FUNCTION_ARGS)
case AT_SetNotNull:
strtype = "SET NOT NULL";
break;
+ case AT_SetAttNotNull:
+ strtype = "SET ATTNOTNULL";
+ break;
case AT_DropExpression:
strtype = "DROP EXPRESSION";
break;
- case AT_CheckNotNull:
- strtype = "CHECK NOT NULL";
- break;
case AT_SetStatistics:
strtype = "SET STATS";
break;
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index cd814ff321c..bfb14349e7c 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -1118,10 +1118,30 @@ ERROR: relation "non_existent" does not exist
-- test checking for null values and primary key
create table atacc1 (test int not null);
alter table atacc1 add constraint "atacc1_pkey" primary key (test);
+\d atacc1
+ Table "public.atacc1"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ test | integer | | not null |
+Indexes:
+ "atacc1_pkey" PRIMARY KEY, btree (test)
+
alter table atacc1 alter column test drop not null;
-ERROR: column "test" is in a primary key
+\d atacc1
+ Table "public.atacc1"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ test | integer | | not null |
+Indexes:
+ "atacc1_pkey" PRIMARY KEY, btree (test)
+
alter table atacc1 drop constraint "atacc1_pkey";
-alter table atacc1 alter column test drop not null;
+\d atacc1
+ Table "public.atacc1"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ test | integer | | |
+
insert into atacc1 values (null);
alter table atacc1 alter test set not null;
ERROR: column "test" of relation "atacc1" contains null values
@@ -1194,20 +1214,6 @@ alter table only parent alter a set not null;
ERROR: column "a" of relation "parent" contains null values
alter table child alter a set not null;
ERROR: column "a" of relation "child" contains null values
-delete from parent;
-alter table only parent alter a set not null;
-insert into parent values (NULL);
-ERROR: null value in column "a" of relation "parent" violates not-null constraint
-DETAIL: Failing row contains (null).
-alter table child alter a set not null;
-insert into child (a, b) values (NULL, 'foo');
-ERROR: null value in column "a" of relation "child" violates not-null constraint
-DETAIL: Failing row contains (null, foo).
-delete from child;
-alter table child alter a set not null;
-insert into child (a, b) values (NULL, 'foo');
-ERROR: null value in column "a" of relation "child" violates not-null constraint
-DETAIL: Failing row contains (null, foo).
drop table child;
drop table parent;
-- test setting and removing default values
@@ -3834,6 +3840,29 @@ Referenced by:
TABLE "ataddindex" CONSTRAINT "ataddindex_ref_id_fkey" FOREIGN KEY (ref_id) REFERENCES ataddindex(id)
DROP TABLE ataddindex;
+CREATE TABLE atnotnull1 ();
+ALTER TABLE atnotnull1
+ ADD COLUMN a INT,
+ ALTER a SET NOT NULL;
+ALTER TABLE atnotnull1
+ ADD COLUMN b INT,
+ ADD NOT NULL b;
+ALTER TABLE atnotnull1
+ ADD COLUMN c INT,
+ ADD PRIMARY KEY (c);
+\d+ atnotnull1
+ Table "public.atnotnull1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | not null | | plain | |
+ b | integer | | not null | | plain | |
+ c | integer | | not null | | plain | |
+Indexes:
+ "atnotnull1_pkey" PRIMARY KEY, btree (c)
+Not-null constraints:
+ "atnotnull1_a_not_null" NOT NULL "a"
+ "atnotnull1_b_not_null" NOT NULL "b"
+
-- cannot drop column that is part of the partition key
CREATE TABLE partitioned (
a int,
@@ -4351,7 +4380,6 @@ ERROR: cannot alter inherited column "b"
-- partitions exist
ALTER TABLE ONLY list_parted2 ALTER b SET NOT NULL;
ERROR: constraint must be added to child tables too
-DETAIL: Column "b" of relation "part_2" is not already NOT NULL.
HINT: Do not specify the ONLY keyword.
ALTER TABLE ONLY list_parted2 ADD CONSTRAINT check_b CHECK (b <> 'zz');
ERROR: constraint must be added to child tables too
diff --git a/src/test/regress/expected/cluster.out b/src/test/regress/expected/cluster.out
index 542c2e098c5..a666d89ef59 100644
--- a/src/test/regress/expected/cluster.out
+++ b/src/test/regress/expected/cluster.out
@@ -247,11 +247,12 @@ ERROR: insert or update on table "clstr_tst" violates foreign key constraint "c
DETAIL: Key (b)=(1111) is not present in table "clstr_tst_s".
SELECT conname FROM pg_constraint WHERE conrelid = 'clstr_tst'::regclass
ORDER BY 1;
- conname
-----------------
+ conname
+----------------------
+ clstr_tst_a_not_null
clstr_tst_con
clstr_tst_pkey
-(2 rows)
+(3 rows)
SELECT relname, relkind,
EXISTS(SELECT 1 FROM pg_class WHERE oid = c.reltoastrelid) AS hastoast
diff --git a/src/test/regress/expected/constraints.out b/src/test/regress/expected/constraints.out
index e6f6602d953..b7de50ad6a6 100644
--- a/src/test/regress/expected/constraints.out
+++ b/src/test/regress/expected/constraints.out
@@ -288,6 +288,39 @@ ERROR: new row for relation "atacc1" violates check constraint "atacc1_test2_ch
DETAIL: Failing row contains (null, 3).
DROP TABLE ATACC1 CASCADE;
NOTICE: drop cascades to table atacc2
+-- NOT NULL NO INHERIT
+CREATE TABLE ATACC1 (a int, not null a no inherit);
+CREATE TABLE ATACC2 () INHERITS (ATACC1);
+\d+ ATACC2
+ Table "public.atacc2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+Inherits: atacc1
+
+DROP TABLE ATACC1, ATACC2;
+CREATE TABLE ATACC1 (a int);
+ALTER TABLE ATACC1 ADD NOT NULL a NO INHERIT;
+CREATE TABLE ATACC2 () INHERITS (ATACC1);
+\d+ ATACC2
+ Table "public.atacc2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+Inherits: atacc1
+
+DROP TABLE ATACC1, ATACC2;
+CREATE TABLE ATACC1 (a int);
+CREATE TABLE ATACC2 () INHERITS (ATACC1);
+ALTER TABLE ATACC1 ADD NOT NULL a NO INHERIT;
+\d+ ATACC2
+ Table "public.atacc2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+Inherits: atacc1
+
+DROP TABLE ATACC1, ATACC2;
--
-- Check constraints on INSERT INTO
--
@@ -754,6 +787,225 @@ ALTER TABLE deferred_excl ADD EXCLUDE (f1 WITH =);
ERROR: could not create exclusion constraint "deferred_excl_f1_excl"
DETAIL: Key (f1)=(3) conflicts with key (f1)=(3).
DROP TABLE deferred_excl;
+-- verify constraints created for NOT NULL clauses
+CREATE TABLE notnull_tbl1 (a INTEGER NOT NULL NOT NULL);
+\d+ notnull_tbl1
+ Table "public.notnull_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | not null | | plain | |
+Not-null constraints:
+ "notnull_tbl1_a_not_null" NOT NULL "a"
+
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+ conname | contype | conkey
+-------------------------+---------+--------
+ notnull_tbl1_a_not_null | n | {1}
+(1 row)
+
+-- no-op
+ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a;
+\d+ notnull_tbl1
+ Table "public.notnull_tbl1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | not null | | plain | |
+Not-null constraints:
+ "notnull_tbl1_a_not_null" NOT NULL "a"
+
+-- duplicate name
+ALTER TABLE notnull_tbl1 ADD COLUMN b INT CONSTRAINT notnull_tbl1_a_not_null NOT NULL;
+ERROR: constraint "notnull_tbl1_a_not_null" for relation "notnull_tbl1" already exists
+-- DROP NOT NULL gets rid of both the attnotnull flag and the constraint itself
+ALTER TABLE notnull_tbl1 ALTER a DROP NOT NULL;
+\d notnull_tbl1
+ Table "public.notnull_tbl1"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | |
+
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+ conname | contype | conkey
+---------+---------+--------
+(0 rows)
+
+-- SET NOT NULL puts both back
+ALTER TABLE notnull_tbl1 ALTER a SET NOT NULL;
+\d notnull_tbl1
+ Table "public.notnull_tbl1"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | not null |
+
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+ conname | contype | conkey
+-------------------------+---------+--------
+ notnull_tbl1_a_not_null | n | {1}
+(1 row)
+
+-- Doing it twice doesn't create a redundant constraint
+ALTER TABLE notnull_tbl1 ALTER a SET NOT NULL;
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+ conname | contype | conkey
+-------------------------+---------+--------
+ notnull_tbl1_a_not_null | n | {1}
+(1 row)
+
+-- Using the "table constraint" syntax also works
+ALTER TABLE notnull_tbl1 ALTER a DROP NOT NULL;
+ALTER TABLE notnull_tbl1 ADD CONSTRAINT foobar NOT NULL a;
+\d notnull_tbl1
+ Table "public.notnull_tbl1"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | not null |
+
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+ conname | contype | conkey
+---------+---------+--------
+ foobar | n | {1}
+(1 row)
+
+DROP TABLE notnull_tbl1;
+-- nope
+CREATE TABLE notnull_tbl2 (a INTEGER CONSTRAINT blah NOT NULL, b INTEGER CONSTRAINT blah NOT NULL);
+ERROR: constraint "blah" for relation "notnull_tbl2" already exists
+CREATE TABLE notnull_tbl2 (a INTEGER PRIMARY KEY);
+ALTER TABLE notnull_tbl2 ALTER a DROP NOT NULL;
+ERROR: column "a" is in a primary key
+CREATE TABLE notnull_tbl3 (a INTEGER NOT NULL, CHECK (a IS NOT NULL));
+ALTER TABLE notnull_tbl3 ALTER A DROP NOT NULL;
+ALTER TABLE notnull_tbl3 ADD b int, ADD CONSTRAINT pk PRIMARY KEY (a, b);
+\d notnull_tbl3
+ Table "public.notnull_tbl3"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | not null |
+ b | integer | | not null |
+Indexes:
+ "pk" PRIMARY KEY, btree (a, b)
+Check constraints:
+ "notnull_tbl3_a_check" CHECK (a IS NOT NULL)
+
+ALTER TABLE notnull_tbl3 DROP CONSTRAINT pk;
+\d notnull_tbl3
+ Table "public.notnull_tbl3"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | |
+ b | integer | | |
+Check constraints:
+ "notnull_tbl3_a_check" CHECK (a IS NOT NULL)
+
+-- Primary keys in parent table cause NOT NULL constraint to spawn on their
+-- children. Verify that they work correctly.
+CREATE TABLE cnn_parent (a int, b int);
+CREATE TABLE cnn_child () INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild (NOT NULL b) INHERITS (cnn_child);
+CREATE TABLE cnn_child2 (NOT NULL a NO INHERIT) INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild2 () INHERITS (cnn_grandchild, cnn_child2);
+NOTICE: merging multiple inherited definitions of column "a"
+NOTICE: merging multiple inherited definitions of column "b"
+ALTER TABLE cnn_parent ADD PRIMARY KEY (b);
+\d+ cnn_grandchild
+ Table "public.cnn_grandchild"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+ b | integer | | not null | | plain | |
+Not-null constraints:
+ "cnn_grandchild_b_not_null" NOT NULL "b" (local, inherited)
+Inherits: cnn_child
+Child tables: cnn_grandchild2
+
+\d+ cnn_grandchild2
+ Table "public.cnn_grandchild2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+ b | integer | | not null | | plain | |
+Not-null constraints:
+ "cnn_grandchild_b_not_null" NOT NULL "b" (inherited)
+Inherits: cnn_grandchild,
+ cnn_child2
+
+ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
+\set VERBOSITY terse
+DROP TABLE cnn_parent CASCADE;
+NOTICE: drop cascades to 4 other objects
+\set VERBOSITY default
+-- As above, but create the primary key ahead of time
+CREATE TABLE cnn_parent (a int, b int PRIMARY KEY);
+CREATE TABLE cnn_child () INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild (NOT NULL b) INHERITS (cnn_child);
+CREATE TABLE cnn_child2 (NOT NULL a NO INHERIT) INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild2 () INHERITS (cnn_grandchild, cnn_child2);
+NOTICE: merging multiple inherited definitions of column "a"
+NOTICE: merging multiple inherited definitions of column "b"
+ALTER TABLE cnn_parent ADD PRIMARY KEY (b);
+ERROR: multiple primary keys for table "cnn_parent" are not allowed
+\d+ cnn_grandchild
+ Table "public.cnn_grandchild"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+ b | integer | | not null | | plain | |
+Not-null constraints:
+ "cnn_grandchild_b_not_null" NOT NULL "b" (local, inherited)
+Inherits: cnn_child
+Child tables: cnn_grandchild2
+
+\d+ cnn_grandchild2
+ Table "public.cnn_grandchild2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+ b | integer | | not null | | plain | |
+Not-null constraints:
+ "cnn_grandchild_b_not_null" NOT NULL "b" (inherited)
+Inherits: cnn_grandchild,
+ cnn_child2
+
+ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
+\set VERBOSITY terse
+DROP TABLE cnn_parent CASCADE;
+NOTICE: drop cascades to 4 other objects
+\set VERBOSITY default
+-- As above, but create the primary key using a UNIQUE index
+CREATE TABLE cnn_parent (a int, b int);
+CREATE TABLE cnn_child () INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild (NOT NULL b) INHERITS (cnn_child);
+CREATE TABLE cnn_child2 (NOT NULL a NO INHERIT) INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild2 () INHERITS (cnn_grandchild, cnn_child2);
+NOTICE: merging multiple inherited definitions of column "a"
+NOTICE: merging multiple inherited definitions of column "b"
+CREATE UNIQUE INDEX b_uq ON cnn_parent (b);
+ALTER TABLE cnn_parent ADD PRIMARY KEY USING INDEX b_uq;
+\d+ cnn_grandchild
+ Table "public.cnn_grandchild"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+ b | integer | | not null | | plain | |
+Not-null constraints:
+ "cnn_grandchild_b_not_null" NOT NULL "b" (local, inherited)
+Inherits: cnn_child
+Child tables: cnn_grandchild2
+
+\d+ cnn_grandchild2
+ Table "public.cnn_grandchild2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+ b | integer | | not null | | plain | |
+Not-null constraints:
+ "cnn_grandchild_b_not_null" NOT NULL "b" (inherited)
+Inherits: cnn_grandchild,
+ cnn_child2
+
+ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
+ERROR: constraint "cnn_parent_pkey" of relation "cnn_parent" does not exist
+-- keeps these tables around, for pg_upgrade testing
-- Comments
-- Setup a low-level role to enforce non-superuser checks.
CREATE ROLE regress_constraint_comments;
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 2a0902ece24..344d05233ad 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -758,22 +758,24 @@ CREATE TABLE part_b PARTITION OF parted (
) FOR VALUES IN ('b');
NOTICE: merging constraint "check_a" with inherited definition
-- conislocal should be false for any merged constraints, true otherwise
-SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY conislocal, coninhcount;
- conislocal | coninhcount
-------------+-------------
- f | 1
- t | 0
-(2 rows)
+SELECT conname, conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY coninhcount DESC, conname;
+ conname | conislocal | coninhcount
+-------------------+------------+-------------
+ check_a | f | 1
+ part_b_b_not_null | t | 1
+ check_b | t | 0
+(3 rows)
-- Once check_b is added to the parent, it should be made non-local for part_b
ALTER TABLE parted ADD CONSTRAINT check_b CHECK (b >= 0);
NOTICE: merging constraint "check_b" with inherited definition
-SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass;
- conislocal | coninhcount
-------------+-------------
- f | 1
- f | 1
-(2 rows)
+SELECT conname, conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY coninhcount DESC, conname;
+ conname | conislocal | coninhcount
+-------------------+------------+-------------
+ check_a | f | 1
+ check_b | f | 1
+ part_b_b_not_null | t | 1
+(3 rows)
-- Neither check_a nor check_b are droppable from part_b
ALTER TABLE part_b DROP CONSTRAINT check_a;
@@ -784,10 +786,11 @@ ERROR: cannot drop inherited constraint "check_b" of relation "part_b"
-- traditional inheritance where they will be left behind, because they would
-- be local constraints.
ALTER TABLE parted DROP CONSTRAINT check_a, DROP CONSTRAINT check_b;
-SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass;
- conislocal | coninhcount
-------------+-------------
-(0 rows)
+SELECT conname, conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY coninhcount DESC, conname;
+ conname | conislocal | coninhcount
+-------------------+------------+-------------
+ part_b_b_not_null | t | 1
+(1 row)
-- specify PARTITION BY for a partition
CREATE TABLE fail_part_col_not_found PARTITION OF parted FOR VALUES IN ('c') PARTITION BY RANGE (c);
@@ -851,6 +854,8 @@ drop table test_part_coll_posix;
b | integer | | not null | 1 | plain | |
Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = 'b'::text))
+Not-null constraints:
+ "part_b_b_not_null" NOT NULL "b" (local, inherited)
-- Both partition bound and partition key in describe output
\d+ part_c
@@ -862,6 +867,8 @@ Partition constraint: ((a IS NOT NULL) AND (a = 'b'::text))
Partition of: parted FOR VALUES IN ('c')
Partition constraint: ((a IS NOT NULL) AND (a = 'c'::text))
Partition key: RANGE (b)
+Not-null constraints:
+ "part_c_b_not_null" NOT NULL "b" (local, inherited)
Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
-- a level-2 partition's constraint will include the parent's expressions
@@ -873,6 +880,8 @@ Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
b | integer | | not null | 0 | plain | |
Partition of: part_c FOR VALUES FROM (1) TO (10)
Partition constraint: ((a IS NOT NULL) AND (a = 'c'::text) AND (b IS NOT NULL) AND (b >= 1) AND (b < 10))
+Not-null constraints:
+ "part_c_b_not_null" NOT NULL "b" (inherited)
-- Show partition count in the parent's describe output
-- Tempted to include \d+ output listing partitions with bound info but
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 0ed94f1d2fb..61956773ffd 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -333,6 +333,8 @@ CREATE TABLE ctlt12_storage (LIKE ctlt1 INCLUDING STORAGE, LIKE ctlt2 INCLUDING
a | text | | not null | | main | |
b | text | | | | extended | |
c | text | | | | external | |
+Not-null constraints:
+ "ctlt12_storage_a_not_null" NOT NULL "a"
CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDING COMMENTS);
\d+ ctlt12_comments
@@ -342,6 +344,8 @@ CREATE TABLE ctlt12_comments (LIKE ctlt1 INCLUDING COMMENTS, LIKE ctlt2 INCLUDIN
a | text | | not null | | extended | | A
b | text | | | | extended | | B
c | text | | | | extended | | C
+Not-null constraints:
+ "ctlt12_comments_a_not_null" NOT NULL "a"
CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INHERITS (ctlt1);
NOTICE: merging column "a" with inherited definition
@@ -355,6 +359,8 @@ NOTICE: merging constraint "ctlt1_a_check" with inherited definition
b | text | | | | extended | | B
Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
+Not-null constraints:
+ "ctlt1_inh_a_not_null" NOT NULL "a" (local, inherited)
Inherits: ctlt1
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt1_inh'::regclass;
@@ -376,6 +382,8 @@ Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
"ctlt3_c_check" CHECK (length(c) < 7)
+Not-null constraints:
+ "ctlt13_inh_a_not_null" NOT NULL "a" (inherited)
Inherits: ctlt1,
ctlt3
@@ -394,6 +402,8 @@ Check constraints:
"ctlt1_a_check" CHECK (length(a) > 2)
"ctlt3_a_check" CHECK (length(a) < 5)
"ctlt3_c_check" CHECK (length(c) < 7)
+Not-null constraints:
+ "ctlt13_like_a_not_null" NOT NULL "a" (inherited)
Inherits: ctlt1
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass;
diff --git a/src/test/regress/expected/event_trigger.out b/src/test/regress/expected/event_trigger.out
index 5a10958df52..2c8a6b22121 100644
--- a/src/test/regress/expected/event_trigger.out
+++ b/src/test/regress/expected/event_trigger.out
@@ -408,6 +408,7 @@ NOTICE: END: command_tag=CREATE SCHEMA type=schema identity=evttrig
NOTICE: END: command_tag=CREATE SEQUENCE type=sequence identity=evttrig.one_col_a_seq
NOTICE: END: command_tag=CREATE SEQUENCE type=sequence identity=evttrig.one_col_c_seq
NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.one
+NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.one
NOTICE: END: command_tag=CREATE INDEX type=index identity=evttrig.one_pkey
NOTICE: END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.one_col_a_seq
NOTICE: END: command_tag=ALTER SEQUENCE type=sequence identity=evttrig.one_col_c_seq
@@ -422,6 +423,7 @@ CREATE TABLE evttrig.parted (
id int PRIMARY KEY)
PARTITION BY RANGE (id);
NOTICE: END: command_tag=CREATE TABLE type=table identity=evttrig.parted
+NOTICE: END: command_tag=ALTER TABLE type=table identity=evttrig.parted
NOTICE: END: command_tag=CREATE INDEX type=index identity=evttrig.parted_pkey
CREATE TABLE evttrig.part_1_10 PARTITION OF evttrig.parted (id)
FOR VALUES FROM (1) TO (10);
diff --git a/src/test/regress/expected/foreign_data.out b/src/test/regress/expected/foreign_data.out
index 5b30ee49f3e..1dfe23cc1e0 100644
--- a/src/test/regress/expected/foreign_data.out
+++ b/src/test/regress/expected/foreign_data.out
@@ -742,6 +742,8 @@ COMMENT ON COLUMN ft1.c1 IS 'ft1.c1';
Check constraints:
"ft1_c2_check" CHECK (c2 <> ''::text)
"ft1_c3_check" CHECK (c3 >= '01-01-1994'::date AND c3 <= '01-31-1994'::date)
+Not-null constraints:
+ "ft1_c1_not_null" NOT NULL "c1"
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -864,6 +866,9 @@ ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 SET STORAGE PLAIN;
Check constraints:
"ft1_c2_check" CHECK (c2 <> ''::text)
"ft1_c3_check" CHECK (c3 >= '01-01-1994'::date AND c3 <= '01-31-1994'::date)
+Not-null constraints:
+ "ft1_c1_not_null" NOT NULL "c1"
+ "ft1_c6_not_null" NOT NULL "c6"
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -1404,6 +1409,8 @@ CREATE FOREIGN TABLE ft2 () INHERITS (fd_pt1)
c1 | integer | | not null | | plain | |
c2 | text | | | | extended | |
c3 | date | | | | plain | |
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1413,6 +1420,8 @@ Child tables: ft2, FOREIGN
c1 | integer | | not null | | | plain | |
c2 | text | | | | | extended | |
c3 | date | | | | | plain | |
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1" (inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1425,6 +1434,8 @@ DROP FOREIGN TABLE ft2;
c1 | integer | | not null | | plain | |
c2 | text | | | | extended | |
c3 | date | | | | plain | |
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
CREATE FOREIGN TABLE ft2 (
c1 integer NOT NULL,
@@ -1438,6 +1449,8 @@ CREATE FOREIGN TABLE ft2 (
c1 | integer | | not null | | | plain | |
c2 | text | | | | | extended | |
c3 | date | | | | | plain | |
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1"
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -1449,6 +1462,8 @@ ALTER FOREIGN TABLE ft2 INHERIT fd_pt1;
c1 | integer | | not null | | plain | |
c2 | text | | | | extended | |
c3 | date | | | | plain | |
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1458,6 +1473,8 @@ Child tables: ft2, FOREIGN
c1 | integer | | not null | | | plain | |
c2 | text | | | | | extended | |
c3 | date | | | | | plain | |
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1479,6 +1496,8 @@ NOTICE: merging column "c3" with inherited definition
c1 | integer | | not null | | | plain | |
c2 | text | | | | | extended | |
c3 | date | | | | | plain | |
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1492,6 +1511,8 @@ Child tables: ct3,
c1 | integer | | not null | | plain | |
c2 | text | | | | extended | |
c3 | date | | | | plain | |
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (inherited)
Inherits: ft2
\d+ ft3
@@ -1501,6 +1522,8 @@ Inherits: ft2
c1 | integer | | not null | | | plain | |
c2 | text | | | | | extended | |
c3 | date | | | | | plain | |
+Not-null constraints:
+ "ft3_c1_not_null" NOT NULL "c1" (local, inherited)
Server: s0
Inherits: ft2
@@ -1522,6 +1545,9 @@ ALTER TABLE fd_pt1 ADD COLUMN c8 integer;
c6 | integer | | | | plain | |
c7 | integer | | not null | | plain | |
c8 | integer | | | | plain | |
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
+ "fd_pt1_c7_not_null" NOT NULL "c7"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1536,6 +1562,9 @@ Child tables: ft2, FOREIGN
c6 | integer | | | | | plain | |
c7 | integer | | not null | | | plain | |
c8 | integer | | | | | plain | |
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
+ "fd_pt1_c7_not_null" NOT NULL "c7" (inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1554,6 +1583,9 @@ Child tables: ct3,
c6 | integer | | | | plain | |
c7 | integer | | not null | | plain | |
c8 | integer | | | | plain | |
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (inherited)
+ "fd_pt1_c7_not_null" NOT NULL "c7" (inherited)
Inherits: ft2
\d+ ft3
@@ -1568,6 +1600,9 @@ Inherits: ft2
c6 | integer | | | | | plain | |
c7 | integer | | not null | | | plain | |
c8 | integer | | | | | plain | |
+Not-null constraints:
+ "ft3_c1_not_null" NOT NULL "c1" (local, inherited)
+ "fd_pt1_c7_not_null" NOT NULL "c7" (inherited)
Server: s0
Inherits: ft2
@@ -1596,6 +1631,9 @@ ALTER TABLE fd_pt1 ALTER COLUMN c8 SET STORAGE EXTERNAL;
c6 | integer | | not null | | plain | |
c7 | integer | | | | plain | |
c8 | text | | | | external | |
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
+ "fd_pt1_c6_not_null" NOT NULL "c6"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1610,6 +1648,9 @@ Child tables: ft2, FOREIGN
c6 | integer | | not null | | | plain | |
c7 | integer | | | | | plain | |
c8 | text | | | | | external | |
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
+ "fd_pt1_c6_not_null" NOT NULL "c6" (inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1629,6 +1670,8 @@ ALTER TABLE fd_pt1 DROP COLUMN c8;
c1 | integer | | not null | | plain | 10000 |
c2 | text | | | | extended | |
c3 | date | | | | plain | |
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1638,6 +1681,8 @@ Child tables: ft2, FOREIGN
c1 | integer | | not null | | | plain | 10000 |
c2 | text | | | | | extended | |
c3 | date | | | | | plain | |
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1652,11 +1697,12 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
FROM pg_class AS pc JOIN pg_constraint AS pgc ON (conrelid = pc.oid)
WHERE pc.relname = 'fd_pt1'
ORDER BY 1,2;
- relname | conname | contype | conislocal | coninhcount | connoinherit
----------+------------+---------+------------+-------------+--------------
- fd_pt1 | fd_pt1chk1 | c | t | 0 | t
- fd_pt1 | fd_pt1chk2 | c | t | 0 | f
-(2 rows)
+ relname | conname | contype | conislocal | coninhcount | connoinherit
+---------+--------------------+---------+------------+-------------+--------------
+ fd_pt1 | fd_pt1_c1_not_null | n | t | 0 | f
+ fd_pt1 | fd_pt1chk1 | c | t | 0 | t
+ fd_pt1 | fd_pt1chk2 | c | t | 0 | f
+(3 rows)
-- child does not inherit NO INHERIT constraints
\d+ fd_pt1
@@ -1669,6 +1715,8 @@ SELECT relname, conname, contype, conislocal, coninhcount, connoinherit
Check constraints:
"fd_pt1chk1" CHECK (c1 > 0) NO INHERIT
"fd_pt1chk2" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1680,6 +1728,8 @@ Child tables: ft2, FOREIGN
c3 | date | | | | | plain | |
Check constraints:
"fd_pt1chk2" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1716,6 +1766,8 @@ ALTER FOREIGN TABLE ft2 INHERIT fd_pt1;
Check constraints:
"fd_pt1chk1" CHECK (c1 > 0) NO INHERIT
"fd_pt1chk2" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1727,6 +1779,8 @@ Child tables: ft2, FOREIGN
c3 | date | | | | | plain | |
Check constraints:
"fd_pt1chk2" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1746,6 +1800,8 @@ ALTER TABLE fd_pt1 ADD CONSTRAINT fd_pt1chk3 CHECK (c2 <> '') NOT VALID;
c3 | date | | | | plain | |
Check constraints:
"fd_pt1chk3" CHECK (c2 <> ''::text) NOT VALID
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1758,6 +1814,8 @@ Child tables: ft2, FOREIGN
Check constraints:
"fd_pt1chk2" CHECK (c2 <> ''::text)
"fd_pt1chk3" CHECK (c2 <> ''::text) NOT VALID
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1773,6 +1831,8 @@ ALTER TABLE fd_pt1 VALIDATE CONSTRAINT fd_pt1chk3;
c3 | date | | | | plain | |
Check constraints:
"fd_pt1chk3" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "c1"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1785,6 +1845,8 @@ Child tables: ft2, FOREIGN
Check constraints:
"fd_pt1chk2" CHECK (c2 <> ''::text)
"fd_pt1chk3" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "c1" (local, inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1804,6 +1866,8 @@ ALTER TABLE fd_pt1 RENAME CONSTRAINT fd_pt1chk3 TO f2_check;
f3 | date | | | | plain | |
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
+Not-null constraints:
+ "fd_pt1_c1_not_null" NOT NULL "f1"
Child tables: ft2, FOREIGN
\d+ ft2
@@ -1816,6 +1880,8 @@ Child tables: ft2, FOREIGN
Check constraints:
"f2_check" CHECK (f2 <> ''::text)
"fd_pt1chk2" CHECK (f2 <> ''::text)
+Not-null constraints:
+ "ft2_c1_not_null" NOT NULL "f1" (local, inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
Inherits: fd_pt1
@@ -1862,6 +1928,8 @@ CREATE FOREIGN TABLE fd_pt2_1 PARTITION OF fd_pt2 FOR VALUES IN (1)
c2 | text | | | | extended | |
c3 | date | | | | plain | |
Partition key: LIST (c1)
+Not-null constraints:
+ "fd_pt2_c1_not_null" NOT NULL "c1"
Partitions: fd_pt2_1 FOR VALUES IN (1), FOREIGN
\d+ fd_pt2_1
@@ -1873,6 +1941,8 @@ Partitions: fd_pt2_1 FOR VALUES IN (1), FOREIGN
c3 | date | | | | | plain | |
Partition of: fd_pt2 FOR VALUES IN (1)
Partition constraint: ((c1 IS NOT NULL) AND (c1 = 1))
+Not-null constraints:
+ "fd_pt2_c1_not_null" NOT NULL "c1" (inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -1892,6 +1962,8 @@ CREATE FOREIGN TABLE fd_pt2_1 (
c2 | text | | | | | extended | |
c3 | date | | | | | plain | |
c4 | character(1) | | | | | extended | |
+Not-null constraints:
+ "fd_pt2_1_c1_not_null" NOT NULL "c1"
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -1907,6 +1979,8 @@ DROP FOREIGN TABLE fd_pt2_1;
c2 | text | | | | extended | |
c3 | date | | | | plain | |
Partition key: LIST (c1)
+Not-null constraints:
+ "fd_pt2_c1_not_null" NOT NULL "c1"
Number of partitions: 0
CREATE FOREIGN TABLE fd_pt2_1 (
@@ -1921,6 +1995,8 @@ CREATE FOREIGN TABLE fd_pt2_1 (
c1 | integer | | not null | | | plain | |
c2 | text | | | | | extended | |
c3 | date | | | | | plain | |
+Not-null constraints:
+ "fd_pt2_1_c1_not_null" NOT NULL "c1"
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -1934,6 +2010,8 @@ ALTER TABLE fd_pt2 ATTACH PARTITION fd_pt2_1 FOR VALUES IN (1);
c2 | text | | | | extended | |
c3 | date | | | | plain | |
Partition key: LIST (c1)
+Not-null constraints:
+ "fd_pt2_c1_not_null" NOT NULL "c1"
Partitions: fd_pt2_1 FOR VALUES IN (1), FOREIGN
\d+ fd_pt2_1
@@ -1945,6 +2023,8 @@ Partitions: fd_pt2_1 FOR VALUES IN (1), FOREIGN
c3 | date | | | | | plain | |
Partition of: fd_pt2 FOR VALUES IN (1)
Partition constraint: ((c1 IS NOT NULL) AND (c1 = 1))
+Not-null constraints:
+ "fd_pt2_1_c1_not_null" NOT NULL "c1" (inherited)
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -1962,6 +2042,8 @@ ALTER TABLE fd_pt2_1 ADD CONSTRAINT p21chk CHECK (c2 <> '');
c2 | text | | | | extended | |
c3 | date | | | | plain | |
Partition key: LIST (c1)
+Not-null constraints:
+ "fd_pt2_c1_not_null" NOT NULL "c1"
Partitions: fd_pt2_1 FOR VALUES IN (1), FOREIGN
\d+ fd_pt2_1
@@ -1975,6 +2057,9 @@ Partition of: fd_pt2 FOR VALUES IN (1)
Partition constraint: ((c1 IS NOT NULL) AND (c1 = 1))
Check constraints:
"p21chk" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "fd_pt2_1_c1_not_null" NOT NULL "c1" (inherited)
+ "fd_pt2_1_c3_not_null" NOT NULL "c3"
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -1992,6 +2077,9 @@ ALTER TABLE fd_pt2 ALTER c2 SET NOT NULL;
c2 | text | | not null | | extended | |
c3 | date | | | | plain | |
Partition key: LIST (c1)
+Not-null constraints:
+ "fd_pt2_c1_not_null" NOT NULL "c1"
+ "fd_pt2_c2_not_null" NOT NULL "c2"
Number of partitions: 0
\d+ fd_pt2_1
@@ -2003,6 +2091,9 @@ Number of partitions: 0
c3 | date | | not null | | | plain | |
Check constraints:
"p21chk" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "fd_pt2_1_c1_not_null" NOT NULL "c1"
+ "fd_pt2_1_c3_not_null" NOT NULL "c3"
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
@@ -2022,6 +2113,9 @@ ALTER TABLE fd_pt2 ADD CONSTRAINT fd_pt2chk1 CHECK (c1 > 0);
Partition key: LIST (c1)
Check constraints:
"fd_pt2chk1" CHECK (c1 > 0)
+Not-null constraints:
+ "fd_pt2_c1_not_null" NOT NULL "c1"
+ "fd_pt2_c2_not_null" NOT NULL "c2"
Number of partitions: 0
\d+ fd_pt2_1
@@ -2033,6 +2127,10 @@ Number of partitions: 0
c3 | date | | not null | | | plain | |
Check constraints:
"p21chk" CHECK (c2 <> ''::text)
+Not-null constraints:
+ "fd_pt2_1_c1_not_null" NOT NULL "c1"
+ "fd_pt2_1_c2_not_null" NOT NULL "c2"
+ "fd_pt2_1_c3_not_null" NOT NULL "c3"
Server: s0
FDW options: (delimiter ',', quote '"', "be quoted" 'value')
diff --git a/src/test/regress/expected/foreign_key.out b/src/test/regress/expected/foreign_key.out
index 12e523c737b..af2a878dd67 100644
--- a/src/test/regress/expected/foreign_key.out
+++ b/src/test/regress/expected/foreign_key.out
@@ -2036,13 +2036,19 @@ ORDER BY co.contype, cr.relname, co.conname, p.conname;
part33_self_fk | parted_self_fk_id_abc_fkey | f | t | parted_self_fk_id_abc_fkey | t | parted_self_fk
part3_self_fk | parted_self_fk_id_abc_fkey | f | t | parted_self_fk_id_abc_fkey | t | parted_self_fk
parted_self_fk | parted_self_fk_id_abc_fkey | f | t | | | parted_self_fk
+ part1_self_fk | part1_self_fk_id_not_null | n | t | | |
+ part2_self_fk | parted_self_fk_id_not_null | n | t | | |
+ part32_self_fk | part3_self_fk_id_not_null | n | t | | |
+ part33_self_fk | part33_self_fk_id_not_null | n | t | | |
+ part3_self_fk | part3_self_fk_id_not_null | n | t | | |
+ parted_self_fk | parted_self_fk_id_not_null | n | t | | |
part1_self_fk | part1_self_fk_pkey | p | t | parted_self_fk_pkey | t |
part2_self_fk | part2_self_fk_pkey | p | t | parted_self_fk_pkey | t |
part32_self_fk | part32_self_fk_pkey | p | t | part3_self_fk_pkey | t |
part33_self_fk | part33_self_fk_pkey | p | t | part3_self_fk_pkey | t |
part3_self_fk | part3_self_fk_pkey | p | t | parted_self_fk_pkey | t |
parted_self_fk | parted_self_fk_pkey | p | t | | |
-(12 rows)
+(18 rows)
-- detach and re-attach multiple times just to ensure everything is kosher
ALTER TABLE parted_self_fk DETACH PARTITION part2_self_fk;
@@ -2065,13 +2071,19 @@ ORDER BY co.contype, cr.relname, co.conname, p.conname;
part33_self_fk | parted_self_fk_id_abc_fkey | f | t | parted_self_fk_id_abc_fkey | t | parted_self_fk
part3_self_fk | parted_self_fk_id_abc_fkey | f | t | parted_self_fk_id_abc_fkey | t | parted_self_fk
parted_self_fk | parted_self_fk_id_abc_fkey | f | t | | | parted_self_fk
+ part1_self_fk | part1_self_fk_id_not_null | n | t | | |
+ part2_self_fk | parted_self_fk_id_not_null | n | t | | |
+ part32_self_fk | part3_self_fk_id_not_null | n | t | | |
+ part33_self_fk | part33_self_fk_id_not_null | n | t | | |
+ part3_self_fk | part3_self_fk_id_not_null | n | t | | |
+ parted_self_fk | parted_self_fk_id_not_null | n | t | | |
part1_self_fk | part1_self_fk_pkey | p | t | parted_self_fk_pkey | t |
part2_self_fk | part2_self_fk_pkey | p | t | parted_self_fk_pkey | t |
part32_self_fk | part32_self_fk_pkey | p | t | part3_self_fk_pkey | t |
part33_self_fk | part33_self_fk_pkey | p | t | part3_self_fk_pkey | t |
part3_self_fk | part3_self_fk_pkey | p | t | parted_self_fk_pkey | t |
parted_self_fk | parted_self_fk_pkey | p | t | | |
-(12 rows)
+(18 rows)
-- Leave this table around, for pg_upgrade/pg_dump tests
-- Test creating a constraint at the parent that already exists in partitions.
diff --git a/src/test/regress/expected/generated.out b/src/test/regress/expected/generated.out
index f5d802b9d14..dc97ed3fe01 100644
--- a/src/test/regress/expected/generated.out
+++ b/src/test/regress/expected/generated.out
@@ -315,6 +315,8 @@ NOTICE: merging column "b" with inherited definition
a | integer | | not null | | plain | |
b | integer | | | generated always as (a * 22) stored | plain | |
x | integer | | | | plain | |
+Not-null constraints:
+ "gtestx_a_not_null" NOT NULL "a" (inherited)
Inherits: gtest1
CREATE TABLE gtestxx_1 (a int NOT NULL, b int);
diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out
index 5f03d8e14fb..7c6e87e8a5b 100644
--- a/src/test/regress/expected/identity.out
+++ b/src/test/regress/expected/identity.out
@@ -506,6 +506,10 @@ TABLE itest8;
f3 | integer | | not null | generated by default as identity | plain | |
f4 | bigint | | not null | generated always as identity | plain | |
f5 | bigint | | | | plain | |
+Not-null constraints:
+ "itest8_f2_not_null" NOT NULL "f2"
+ "itest8_f3_not_null" NOT NULL "f3"
+ "itest8_f4_not_null" NOT NULL "f4"
\d itest8_f2_seq
Sequence "public.itest8_f2_seq"
diff --git a/src/test/regress/expected/indexing.out b/src/test/regress/expected/indexing.out
index 598c75279af..087f955b1e6 100644
--- a/src/test/regress/expected/indexing.out
+++ b/src/test/regress/expected/indexing.out
@@ -1116,16 +1116,18 @@ create table idxpart3 (b int not null, a int not null);
alter table idxpart attach partition idxpart3 for values from (20, 20) to (30, 30);
select conname, contype, conrelid::regclass, conindid::regclass, conkey
from pg_constraint where conrelid::regclass::text like 'idxpart%'
- order by conname;
- conname | contype | conrelid | conindid | conkey
-----------------+---------+-----------+----------------+--------
- idxpart1_pkey | p | idxpart1 | idxpart1_pkey | {1,2}
- idxpart21_pkey | p | idxpart21 | idxpart21_pkey | {1,2}
- idxpart22_pkey | p | idxpart22 | idxpart22_pkey | {1,2}
- idxpart2_pkey | p | idxpart2 | idxpart2_pkey | {1,2}
- idxpart3_pkey | p | idxpart3 | idxpart3_pkey | {2,1}
- idxpart_pkey | p | idxpart | idxpart_pkey | {1,2}
-(6 rows)
+ order by conrelid::regclass::text, conname;
+ conname | contype | conrelid | conindid | conkey
+---------------------+---------+-----------+----------------+--------
+ idxpart_pkey | p | idxpart | idxpart_pkey | {1,2}
+ idxpart1_pkey | p | idxpart1 | idxpart1_pkey | {1,2}
+ idxpart2_pkey | p | idxpart2 | idxpart2_pkey | {1,2}
+ idxpart21_pkey | p | idxpart21 | idxpart21_pkey | {1,2}
+ idxpart22_pkey | p | idxpart22 | idxpart22_pkey | {1,2}
+ idxpart3_a_not_null | n | idxpart3 | - | {2}
+ idxpart3_b_not_null | n | idxpart3 | - | {1}
+ idxpart3_pkey | p | idxpart3 | idxpart3_pkey | {2,1}
+(8 rows)
drop table idxpart;
-- Verify that multi-layer partitioning honors the requirement that all
@@ -1258,12 +1260,21 @@ create table idxpart (a int) partition by range (a);
create table idxpart0 (like idxpart);
alter table idxpart0 add unique (a);
alter table idxpart attach partition idxpart0 default;
-alter table only idxpart add primary key (a); -- fail, no NOT NULL constraint
-ERROR: constraint must be added to child tables too
-DETAIL: Column "a" of relation "idxpart0" is not already NOT NULL.
-HINT: Do not specify the ONLY keyword.
+alter table only idxpart add primary key (a); -- works, but idxpart0.a is nullable
+\d idxpart0
+ Table "public.idxpart0"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ a | integer | | |
+Partition of: idxpart DEFAULT
+Indexes:
+ "idxpart0_a_key" UNIQUE CONSTRAINT, btree (a)
+
+alter index idxpart_pkey attach partition idxpart0_a_key; -- fails, lacks NOT NULL
+ERROR: invalid primary key definition
+DETAIL: Column "a" of relation "idxpart0" is not marked NOT NULL.
alter table idxpart0 alter column a set not null;
-alter table only idxpart add primary key (a); -- now it works
+alter index idxpart_pkey attach partition idxpart0_a_key;
alter table idxpart0 alter column a drop not null; -- fail, pkey needs it
ERROR: column "a" is marked NOT NULL in parent table
drop table idxpart;
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index a8283b77103..b084cb6a6f3 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1956,6 +1956,448 @@ select * from cnullparent where f1 = 2;
drop table cnullparent cascade;
NOTICE: drop cascades to table cnullchild
--
+-- Test inheritance of NOT NULL constraints
+--
+create table pp1 (f1 int);
+create table cc1 (f2 text, f3 int) inherits (pp1);
+\d cc1
+ Table "public.cc1"
+ Column | Type | Collation | Nullable | Default
+--------+---------+-----------+----------+---------
+ f1 | integer | | |
+ f2 | text | | |
+ f3 | integer | | |
+Inherits: pp1
+
+create table cc2(f4 float) inherits(pp1,cc1);
+NOTICE: merging multiple inherited definitions of column "f1"
+\d cc2
+ Table "public.cc2"
+ Column | Type | Collation | Nullable | Default
+--------+------------------+-----------+----------+---------
+ f1 | integer | | |
+ f2 | text | | |
+ f3 | integer | | |
+ f4 | double precision | | |
+Inherits: pp1,
+ cc1
+
+-- named NOT NULL constraint
+alter table cc1 add column a2 int constraint nn not null;
+\d+ cc1
+ Table "public.cc1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | | | plain | |
+ f2 | text | | | | extended | |
+ f3 | integer | | | | plain | |
+ a2 | integer | | not null | | plain | |
+Not-null constraints:
+ "nn" NOT NULL "a2"
+Inherits: pp1
+Child tables: cc2
+
+\d+ cc2
+ Table "public.cc2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+------------------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | | | plain | |
+ f2 | text | | | | extended | |
+ f3 | integer | | | | plain | |
+ f4 | double precision | | | | plain | |
+ a2 | integer | | not null | | plain | |
+Not-null constraints:
+ "nn" NOT NULL "a2" (inherited)
+Inherits: pp1,
+ cc1
+
+alter table pp1 alter column f1 set not null;
+\d+ pp1
+ Table "public.pp1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+Not-null constraints:
+ "pp1_f1_not_null" NOT NULL "f1"
+Child tables: cc1,
+ cc2
+
+\d+ cc1
+ Table "public.cc1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+ f2 | text | | | | extended | |
+ f3 | integer | | | | plain | |
+ a2 | integer | | not null | | plain | |
+Not-null constraints:
+ "pp1_f1_not_null" NOT NULL "f1" (inherited)
+ "nn" NOT NULL "a2"
+Inherits: pp1
+Child tables: cc2
+
+\d+ cc2
+ Table "public.cc2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+------------------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+ f2 | text | | | | extended | |
+ f3 | integer | | | | plain | |
+ f4 | double precision | | | | plain | |
+ a2 | integer | | not null | | plain | |
+Not-null constraints:
+ "pp1_f1_not_null" NOT NULL "f1" (inherited)
+ "nn" NOT NULL "a2" (inherited)
+Inherits: pp1,
+ cc1
+
+-- remove constraint from cc2: no dice, it's inherited
+alter table cc2 alter column a2 drop not null;
+ERROR: cannot drop inherited constraint "nn" of relation "cc2"
+-- remove constraint cc1, should succeed
+alter table cc1 alter column a2 drop not null;
+\d+ cc1
+ Table "public.cc1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+ f2 | text | | | | extended | |
+ f3 | integer | | | | plain | |
+ a2 | integer | | | | plain | |
+Not-null constraints:
+ "pp1_f1_not_null" NOT NULL "f1" (inherited)
+Inherits: pp1
+Child tables: cc2
+
+-- same for cc2
+alter table cc2 alter column f1 drop not null;
+ERROR: cannot drop inherited constraint "pp1_f1_not_null" of relation "cc2"
+\d+ cc2
+ Table "public.cc2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+------------------+-----------+----------+---------+----------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+ f2 | text | | | | extended | |
+ f3 | integer | | | | plain | |
+ f4 | double precision | | | | plain | |
+ a2 | integer | | | | plain | |
+Not-null constraints:
+ "pp1_f1_not_null" NOT NULL "f1" (inherited)
+Inherits: pp1,
+ cc1
+
+-- remove from cc1, should fail again
+alter table cc1 alter column f1 drop not null;
+ERROR: cannot drop inherited constraint "pp1_f1_not_null" of relation "cc1"
+-- remove from pp1, should succeed
+alter table pp1 alter column f1 drop not null;
+\d+ pp1
+ Table "public.pp1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | | | plain | |
+Child tables: cc1,
+ cc2
+
+alter table pp1 add primary key (f1);
+-- Leave these tables around, for pg_upgrade testing
+-- Test the same constraint name for different columns in different parents
+create table inh_parent1(a int constraint nn not null);
+create table inh_parent2(b int constraint nn not null);
+create table inh_child () inherits (inh_parent1, inh_parent2);
+\d+ inh_child
+ Table "public.inh_child"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | not null | | plain | |
+ b | integer | | not null | | plain | |
+Not-null constraints:
+ "nn" NOT NULL "a" (inherited)
+ "inh_child_b_not_null" NOT NULL "b" (inherited)
+Inherits: inh_parent1,
+ inh_parent2
+
+drop table inh_parent1, inh_parent2, inh_child;
+-- Test multiple parents with overlapping primary keys
+create table inh_parent1(a int, b int, c int, primary key (a, b));
+create table inh_parent2(d int, e int, b int, primary key (d, b));
+create table inh_child() inherits (inh_parent1, inh_parent2);
+NOTICE: merging multiple inherited definitions of column "b"
+select conrelid::regclass, conname, contype, conkey,
+ coninhcount, conislocal, connoinherit
+ from pg_constraint where contype in ('n','p') and
+ conrelid::regclass::text in ('inh_child', 'inh_parent1', 'inh_parent2')
+ order by 1, 2;
+ conrelid | conname | contype | conkey | coninhcount | conislocal | connoinherit
+-------------+----------------------+---------+--------+-------------+------------+--------------
+ inh_parent1 | inh_parent1_pkey | p | {1,2} | 0 | t | t
+ inh_parent2 | inh_parent2_pkey | p | {1,3} | 0 | t | t
+ inh_child | inh_child_a_not_null | n | {1} | 1 | f | f
+ inh_child | inh_child_b_not_null | n | {2} | 2 | f | f
+ inh_child | inh_child_d_not_null | n | {4} | 1 | f | f
+(5 rows)
+
+\d+ inh_child
+ Table "public.inh_child"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | not null | | plain | |
+ b | integer | | not null | | plain | |
+ c | integer | | | | plain | |
+ d | integer | | not null | | plain | |
+ e | integer | | | | plain | |
+Not-null constraints:
+ "inh_child_a_not_null" NOT NULL "a" (inherited)
+ "inh_child_b_not_null" NOT NULL "b" (inherited)
+ "inh_child_d_not_null" NOT NULL "d" (inherited)
+Inherits: inh_parent1,
+ inh_parent2
+
+drop table inh_parent1, inh_parent2, inh_child;
+-- NOT NULL NO INHERIT
+create table inh_nn_parent(a int);
+create table inh_nn_child() inherits (inh_nn_parent);
+alter table inh_nn_parent add not null a no inherit;
+create table inh_nn_child2() inherits (inh_nn_parent);
+select conrelid::regclass, conname, contype, conkey,
+ (select attname from pg_attribute where attrelid = conrelid and attnum = conkey[1]),
+ coninhcount, conislocal, connoinherit
+ from pg_constraint where contype = 'n' and
+ conrelid::regclass::text like 'inh\_nn\_%'
+ order by 2, 1;
+ conrelid | conname | contype | conkey | attname | coninhcount | conislocal | connoinherit
+---------------+--------------------------+---------+--------+---------+-------------+------------+--------------
+ inh_nn_parent | inh_nn_parent_a_not_null | n | {1} | a | 0 | t | t
+(1 row)
+
+\d+ inh_nn*
+ Table "public.inh_nn_child"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+Inherits: inh_nn_parent
+
+ Table "public.inh_nn_child2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+Inherits: inh_nn_parent
+
+ Table "public.inh_nn_parent"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | not null | | plain | |
+Not-null constraints:
+ "inh_nn_parent_a_not_null" NOT NULL "a" NO INHERIT
+Child tables: inh_nn_child,
+ inh_nn_child2
+
+drop table inh_nn_parent, inh_nn_child, inh_nn_child2;
+--
+-- test inherit/deinherit
+--
+create table inh_parent(f1 int);
+create table inh_child1(f1 int not null);
+create table inh_child2(f1 int);
+-- inh_child1 should have not null constraint
+alter table inh_child1 inherit inh_parent;
+-- should fail, missing NOT NULL constraint
+alter table inh_child2 inherit inh_child1;
+ERROR: column "f1" in child table must be marked NOT NULL
+alter table inh_child2 alter column f1 set not null;
+alter table inh_child2 inherit inh_child1;
+-- add NOT NULL constraint recursively
+alter table inh_parent alter column f1 set not null;
+\d+ inh_parent
+ Table "public.inh_parent"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+Not-null constraints:
+ "inh_parent_f1_not_null" NOT NULL "f1"
+Child tables: inh_child1
+
+\d+ inh_child1
+ Table "public.inh_child1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+Not-null constraints:
+ "inh_child1_f1_not_null" NOT NULL "f1" (local, inherited)
+Inherits: inh_parent
+Child tables: inh_child2
+
+\d+ inh_child2
+ Table "public.inh_child2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+Not-null constraints:
+ "inh_child2_f1_not_null" NOT NULL "f1" (local, inherited)
+Inherits: inh_child1
+
+select conrelid::regclass, conname, contype, coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid in ('inh_parent'::regclass, 'inh_child1'::regclass, 'inh_child2'::regclass)
+ order by 2, 1;
+ conrelid | conname | contype | coninhcount | conislocal
+------------+------------------------+---------+-------------+------------
+ inh_child1 | inh_child1_f1_not_null | n | 1 | t
+ inh_child2 | inh_child2_f1_not_null | n | 1 | t
+ inh_parent | inh_parent_f1_not_null | n | 0 | t
+(3 rows)
+
+--
+-- test deinherit procedure
+--
+-- deinherit inh_child1
+create table inh_grandchld () inherits (inh_child1);
+alter table inh_child1 no inherit inh_parent;
+\d+ inh_parent
+ Table "public.inh_parent"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+Not-null constraints:
+ "inh_parent_f1_not_null" NOT NULL "f1"
+
+\d+ inh_child1
+ Table "public.inh_child1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+Not-null constraints:
+ "inh_child1_f1_not_null" NOT NULL "f1"
+Child tables: inh_child2,
+ inh_grandchld
+
+\d+ inh_child2
+ Table "public.inh_child2"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ f1 | integer | | not null | | plain | |
+Not-null constraints:
+ "inh_child2_f1_not_null" NOT NULL "f1" (local, inherited)
+Inherits: inh_child1
+
+select conrelid::regclass, conname, contype, coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid::regclass::text in ('inh_parent', 'inh_child1', 'inh_child2', 'inh_grandchld')
+ order by 2, 1;
+ conrelid | conname | contype | coninhcount | conislocal
+---------------+------------------------+---------+-------------+------------
+ inh_child1 | inh_child1_f1_not_null | n | 0 | t
+ inh_grandchld | inh_child1_f1_not_null | n | 1 | f
+ inh_child2 | inh_child2_f1_not_null | n | 1 | t
+ inh_parent | inh_parent_f1_not_null | n | 0 | t
+(4 rows)
+
+drop table inh_parent, inh_child1, inh_child2, inh_grandchld;
+-- a PK in parent must have a not-null in child that it can mark inherited
+create table inh_parent (a int primary key);
+create table inh_child (a int primary key);
+alter table inh_child inherit inh_parent; -- nope
+ERROR: column "a" in child table must be marked NOT NULL
+alter table inh_child alter a set not null;
+alter table inh_child inherit inh_parent; -- now it works
+drop table inh_parent, inh_child;
+--
+-- test multi inheritance tree
+--
+create table inh_parent(f1 int not null);
+create table inh_child1() inherits(inh_parent);
+create table inh_child2() inherits(inh_parent);
+create table inh_grandchld() inherits(inh_child1, inh_child2);
+NOTICE: merging multiple inherited definitions of column "f1"
+-- show constraint info
+select conrelid::regclass, conname, contype, coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid in ('inh_parent'::regclass, 'inh_child1'::regclass, 'inh_child2'::regclass, 'inh_grandchld'::regclass)
+ order by 2, conrelid::regclass::text;
+ conrelid | conname | contype | coninhcount | conislocal
+---------------+------------------------+---------+-------------+------------
+ inh_child1 | inh_parent_f1_not_null | n | 1 | f
+ inh_child2 | inh_parent_f1_not_null | n | 1 | f
+ inh_grandchld | inh_parent_f1_not_null | n | 2 | f
+ inh_parent | inh_parent_f1_not_null | n | 0 | t
+(4 rows)
+
+drop table inh_parent cascade;
+NOTICE: drop cascades to 3 other objects
+DETAIL: drop cascades to table inh_child1
+drop cascades to table inh_child2
+drop cascades to table inh_grandchld
+-- test child table with inherited columns and
+-- with explicitly specified not null constraints
+create table inh_parent_1(f1 int);
+create table inh_parent_2(f2 text);
+create table inh_child(f1 int not null, f2 text not null) inherits(inh_parent_1, inh_parent_2);
+NOTICE: merging column "f1" with inherited definition
+NOTICE: merging column "f2" with inherited definition
+-- show constraint info
+select conrelid::regclass, conname, contype, coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid in ('inh_parent_1'::regclass, 'inh_parent_2'::regclass, 'inh_child'::regclass)
+ order by 2, conrelid::regclass::text;
+ conrelid | conname | contype | coninhcount | conislocal
+-----------+-----------------------+---------+-------------+------------
+ inh_child | inh_child_f1_not_null | n | 0 | t
+ inh_child | inh_child_f2_not_null | n | 0 | t
+(2 rows)
+
+-- also drops inh_child table
+drop table inh_parent_1 cascade;
+NOTICE: drop cascades to table inh_child
+drop table inh_parent_2;
+-- test multi layer inheritance tree
+create table inh_p1(f1 int not null);
+create table inh_p2(f1 int not null);
+create table inh_p3(f2 int);
+create table inh_p4(f1 int not null, f3 text not null);
+create table inh_multiparent() inherits(inh_p1, inh_p2, inh_p3, inh_p4);
+NOTICE: merging multiple inherited definitions of column "f1"
+NOTICE: merging multiple inherited definitions of column "f1"
+-- constraint on f1 should have three parents
+select conrelid::regclass, contype, conname,
+ (select attname from pg_attribute where attrelid = conrelid and attnum = conkey[1]),
+ coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid::regclass in ('inh_p1', 'inh_p2', 'inh_p3', 'inh_p4',
+ 'inh_multiparent')
+ order by conrelid::regclass::text, conname;
+ conrelid | contype | conname | attname | coninhcount | conislocal
+-----------------+---------+--------------------+---------+-------------+------------
+ inh_multiparent | n | inh_p1_f1_not_null | f1 | 3 | f
+ inh_multiparent | n | inh_p4_f3_not_null | f3 | 1 | f
+ inh_p1 | n | inh_p1_f1_not_null | f1 | 0 | t
+ inh_p2 | n | inh_p2_f1_not_null | f1 | 0 | t
+ inh_p4 | n | inh_p4_f1_not_null | f1 | 0 | t
+ inh_p4 | n | inh_p4_f3_not_null | f3 | 0 | t
+(6 rows)
+
+create table inh_multiparent2 (a int not null, f1 int) inherits(inh_p3, inh_multiparent);
+NOTICE: merging multiple inherited definitions of column "f2"
+NOTICE: merging column "f1" with inherited definition
+select conrelid::regclass, contype, conname,
+ (select attname from pg_attribute where attrelid = conrelid and attnum = conkey[1]),
+ coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid::regclass in ('inh_p3', 'inh_multiparent', 'inh_multiparent2')
+ order by conrelid::regclass::text, conname;
+ conrelid | contype | conname | attname | coninhcount | conislocal
+------------------+---------+-----------------------------+---------+-------------+------------
+ inh_multiparent | n | inh_p1_f1_not_null | f1 | 3 | f
+ inh_multiparent | n | inh_p4_f3_not_null | f3 | 1 | f
+ inh_multiparent2 | n | inh_multiparent2_a_not_null | a | 0 | t
+ inh_multiparent2 | n | inh_p1_f1_not_null | f1 | 1 | f
+ inh_multiparent2 | n | inh_p4_f3_not_null | f3 | 1 | f
+(5 rows)
+
+drop table inh_p1, inh_p2, inh_p3, inh_p4 cascade;
+NOTICE: drop cascades to 2 other objects
+DETAIL: drop cascades to table inh_multiparent
+drop cascades to table inh_multiparent2
+--
-- Check use of temporary tables with inheritance trees
--
create table inh_perm_parent (a1 int);
diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out
index 69dc6cfd859..16361a91f9f 100644
--- a/src/test/regress/expected/publication.out
+++ b/src/test/regress/expected/publication.out
@@ -193,6 +193,8 @@ Indexes:
"testpub_tbl2_pkey" PRIMARY KEY, btree (id)
Publications:
"testpub_foralltables"
+Not-null constraints:
+ "testpub_tbl2_id_not_null" NOT NULL "id"
\dRp+ testpub_foralltables
Publication testpub_foralltables
@@ -1147,6 +1149,8 @@ Publications:
"testpib_ins_trunct"
"testpub_default"
"testpub_fortbl"
+Not-null constraints:
+ "testpub_tbl1_id_not_null" NOT NULL "id"
\dRp+ testpub_default
Publication testpub_default
@@ -1172,6 +1176,8 @@ Indexes:
Publications:
"testpib_ins_trunct"
"testpub_fortbl"
+Not-null constraints:
+ "testpub_tbl1_id_not_null" NOT NULL "id"
-- verify relation cache invalidation when a primary key is added using
-- an existing index
diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out
index 7d798ef2a5b..6038bf8e9f7 100644
--- a/src/test/regress/expected/replica_identity.out
+++ b/src/test/regress/expected/replica_identity.out
@@ -170,6 +170,10 @@ Indexes:
"test_replica_identity_partial" UNIQUE, btree (keya, keyb) WHERE keyb <> '3'::text
"test_replica_identity_unique_defer" UNIQUE CONSTRAINT, btree (keya, keyb) DEFERRABLE
"test_replica_identity_unique_nondefer" UNIQUE CONSTRAINT, btree (keya, keyb)
+Not-null constraints:
+ "test_replica_identity_id_not_null" NOT NULL "id"
+ "test_replica_identity_keya_not_null" NOT NULL "keya"
+ "test_replica_identity_keyb_not_null" NOT NULL "keyb"
Replica Identity: FULL
ALTER TABLE test_replica_identity REPLICA IDENTITY NOTHING;
@@ -227,6 +231,9 @@ Indexes:
-- used as replica identity.
ALTER TABLE test_replica_identity3 ALTER COLUMN id DROP NOT NULL;
ERROR: column "id" is in index used as replica identity
+-- but it's OK when the identity is FULL
+ALTER TABLE test_replica_identity3 REPLICA IDENTITY FULL;
+ALTER TABLE test_replica_identity3 ALTER COLUMN id DROP NOT NULL;
--
-- Test that replica identity can be set on an index that's not yet valid.
-- (This matches the way pg_dump will try to dump a partitioned table.)
@@ -249,6 +256,8 @@ ALTER TABLE ONLY test_replica_identity4_1
Partition key: LIST (id)
Indexes:
"test_replica_identity4_pkey" PRIMARY KEY, btree (id) INVALID REPLICA IDENTITY
+Not-null constraints:
+ "test_replica_identity4_id_not_null" NOT NULL "id"
Partitions: test_replica_identity4_1 FOR VALUES IN (1)
ALTER INDEX test_replica_identity4_pkey
@@ -261,10 +270,25 @@ ALTER INDEX test_replica_identity4_pkey
Partition key: LIST (id)
Indexes:
"test_replica_identity4_pkey" PRIMARY KEY, btree (id) REPLICA IDENTITY
+Not-null constraints:
+ "test_replica_identity4_id_not_null" NOT NULL "id"
Partitions: test_replica_identity4_1 FOR VALUES IN (1)
+-- Dropping the primary key is not allowed if that would leave the replica
+-- identity as nullable
+CREATE TABLE test_replica_identity5 (a int not null, b int, c int,
+ PRIMARY KEY (b, c));
+CREATE UNIQUE INDEX test_replica_identity5_a_b_key ON test_replica_identity5 (a, b);
+ALTER TABLE test_replica_identity5 REPLICA IDENTITY USING INDEX test_replica_identity5_a_b_key;
+ALTER TABLE test_replica_identity5 DROP CONSTRAINT test_replica_identity5_pkey;
+ERROR: column "b" is in index used as replica identity
+ALTER TABLE test_replica_identity5 ALTER b SET NOT NULL;
+ALTER TABLE test_replica_identity5 DROP CONSTRAINT test_replica_identity5_pkey;
+ALTER TABLE test_replica_identity5 ALTER b DROP NOT NULL;
+ERROR: column "b" is in index used as replica identity
DROP TABLE test_replica_identity;
DROP TABLE test_replica_identity2;
DROP TABLE test_replica_identity3;
DROP TABLE test_replica_identity4;
+DROP TABLE test_replica_identity5;
DROP TABLE test_replica_identity_othertable;
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index 97ca9bf72c5..6988128aa4c 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -955,6 +955,8 @@ Policies:
POLICY "pp1r" AS RESTRICTIVE
TO regress_rls_dave
USING ((cid < 55))
+Not-null constraints:
+ "part_document_dlevel_not_null" NOT NULL "dlevel"
Partitions: part_document_fiction FOR VALUES FROM (11) TO (12),
part_document_nonfiction FOR VALUES FROM (99) TO (100),
part_document_satire FOR VALUES FROM (55) TO (56)
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index ff8c4984191..eb8c3347dfd 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -850,9 +850,11 @@ alter table non_existent alter column bar drop not null;
-- test checking for null values and primary key
create table atacc1 (test int not null);
alter table atacc1 add constraint "atacc1_pkey" primary key (test);
+\d atacc1
alter table atacc1 alter column test drop not null;
+\d atacc1
alter table atacc1 drop constraint "atacc1_pkey";
-alter table atacc1 alter column test drop not null;
+\d atacc1
insert into atacc1 values (null);
alter table atacc1 alter test set not null;
delete from atacc1;
@@ -917,14 +919,6 @@ insert into parent values (NULL);
insert into child (a, b) values (NULL, 'foo');
alter table only parent alter a set not null;
alter table child alter a set not null;
-delete from parent;
-alter table only parent alter a set not null;
-insert into parent values (NULL);
-alter table child alter a set not null;
-insert into child (a, b) values (NULL, 'foo');
-delete from child;
-alter table child alter a set not null;
-insert into child (a, b) values (NULL, 'foo');
drop table child;
drop table parent;
@@ -2342,6 +2336,18 @@ ALTER TABLE ataddindex
\d ataddindex
DROP TABLE ataddindex;
+CREATE TABLE atnotnull1 ();
+ALTER TABLE atnotnull1
+ ADD COLUMN a INT,
+ ALTER a SET NOT NULL;
+ALTER TABLE atnotnull1
+ ADD COLUMN b INT,
+ ADD NOT NULL b;
+ALTER TABLE atnotnull1
+ ADD COLUMN c INT,
+ ADD PRIMARY KEY (c);
+\d+ atnotnull1
+
-- cannot drop column that is part of the partition key
CREATE TABLE partitioned (
a int,
diff --git a/src/test/regress/sql/constraints.sql b/src/test/regress/sql/constraints.sql
index 5ffcd4ffc7b..782699a4377 100644
--- a/src/test/regress/sql/constraints.sql
+++ b/src/test/regress/sql/constraints.sql
@@ -196,6 +196,22 @@ INSERT INTO ATACC2 (TEST2) VALUES (3);
INSERT INTO ATACC1 (TEST2) VALUES (3);
DROP TABLE ATACC1 CASCADE;
+-- NOT NULL NO INHERIT
+CREATE TABLE ATACC1 (a int, not null a no inherit);
+CREATE TABLE ATACC2 () INHERITS (ATACC1);
+\d+ ATACC2
+DROP TABLE ATACC1, ATACC2;
+CREATE TABLE ATACC1 (a int);
+ALTER TABLE ATACC1 ADD NOT NULL a NO INHERIT;
+CREATE TABLE ATACC2 () INHERITS (ATACC1);
+\d+ ATACC2
+DROP TABLE ATACC1, ATACC2;
+CREATE TABLE ATACC1 (a int);
+CREATE TABLE ATACC2 () INHERITS (ATACC1);
+ALTER TABLE ATACC1 ADD NOT NULL a NO INHERIT;
+\d+ ATACC2
+DROP TABLE ATACC1, ATACC2;
+
--
-- Check constraints on INSERT INTO
--
@@ -556,6 +572,92 @@ ALTER TABLE deferred_excl ADD EXCLUDE (f1 WITH =);
DROP TABLE deferred_excl;
+-- verify constraints created for NOT NULL clauses
+CREATE TABLE notnull_tbl1 (a INTEGER NOT NULL NOT NULL);
+\d+ notnull_tbl1
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+-- no-op
+ALTER TABLE notnull_tbl1 ADD CONSTRAINT nn NOT NULL a;
+\d+ notnull_tbl1
+-- duplicate name
+ALTER TABLE notnull_tbl1 ADD COLUMN b INT CONSTRAINT notnull_tbl1_a_not_null NOT NULL;
+-- DROP NOT NULL gets rid of both the attnotnull flag and the constraint itself
+ALTER TABLE notnull_tbl1 ALTER a DROP NOT NULL;
+\d notnull_tbl1
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+-- SET NOT NULL puts both back
+ALTER TABLE notnull_tbl1 ALTER a SET NOT NULL;
+\d notnull_tbl1
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+-- Doing it twice doesn't create a redundant constraint
+ALTER TABLE notnull_tbl1 ALTER a SET NOT NULL;
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+-- Using the "table constraint" syntax also works
+ALTER TABLE notnull_tbl1 ALTER a DROP NOT NULL;
+ALTER TABLE notnull_tbl1 ADD CONSTRAINT foobar NOT NULL a;
+\d notnull_tbl1
+select conname, contype, conkey from pg_constraint where conrelid = 'notnull_tbl1'::regclass;
+DROP TABLE notnull_tbl1;
+
+-- nope
+CREATE TABLE notnull_tbl2 (a INTEGER CONSTRAINT blah NOT NULL, b INTEGER CONSTRAINT blah NOT NULL);
+
+CREATE TABLE notnull_tbl2 (a INTEGER PRIMARY KEY);
+ALTER TABLE notnull_tbl2 ALTER a DROP NOT NULL;
+
+CREATE TABLE notnull_tbl3 (a INTEGER NOT NULL, CHECK (a IS NOT NULL));
+ALTER TABLE notnull_tbl3 ALTER A DROP NOT NULL;
+ALTER TABLE notnull_tbl3 ADD b int, ADD CONSTRAINT pk PRIMARY KEY (a, b);
+\d notnull_tbl3
+ALTER TABLE notnull_tbl3 DROP CONSTRAINT pk;
+\d notnull_tbl3
+
+-- Primary keys in parent table cause NOT NULL constraint to spawn on their
+-- children. Verify that they work correctly.
+CREATE TABLE cnn_parent (a int, b int);
+CREATE TABLE cnn_child () INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild (NOT NULL b) INHERITS (cnn_child);
+CREATE TABLE cnn_child2 (NOT NULL a NO INHERIT) INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild2 () INHERITS (cnn_grandchild, cnn_child2);
+
+ALTER TABLE cnn_parent ADD PRIMARY KEY (b);
+\d+ cnn_grandchild
+\d+ cnn_grandchild2
+ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
+\set VERBOSITY terse
+DROP TABLE cnn_parent CASCADE;
+\set VERBOSITY default
+
+-- As above, but create the primary key ahead of time
+CREATE TABLE cnn_parent (a int, b int PRIMARY KEY);
+CREATE TABLE cnn_child () INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild (NOT NULL b) INHERITS (cnn_child);
+CREATE TABLE cnn_child2 (NOT NULL a NO INHERIT) INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild2 () INHERITS (cnn_grandchild, cnn_child2);
+
+ALTER TABLE cnn_parent ADD PRIMARY KEY (b);
+\d+ cnn_grandchild
+\d+ cnn_grandchild2
+ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
+\set VERBOSITY terse
+DROP TABLE cnn_parent CASCADE;
+\set VERBOSITY default
+
+-- As above, but create the primary key using a UNIQUE index
+CREATE TABLE cnn_parent (a int, b int);
+CREATE TABLE cnn_child () INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild (NOT NULL b) INHERITS (cnn_child);
+CREATE TABLE cnn_child2 (NOT NULL a NO INHERIT) INHERITS (cnn_parent);
+CREATE TABLE cnn_grandchild2 () INHERITS (cnn_grandchild, cnn_child2);
+
+CREATE UNIQUE INDEX b_uq ON cnn_parent (b);
+ALTER TABLE cnn_parent ADD PRIMARY KEY USING INDEX b_uq;
+\d+ cnn_grandchild
+\d+ cnn_grandchild2
+ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
+-- keeps these tables around, for pg_upgrade testing
+
+
-- Comments
-- Setup a low-level role to enforce non-superuser checks.
CREATE ROLE regress_constraint_comments;
diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql
index 82ada476618..1fd4cbfa7ef 100644
--- a/src/test/regress/sql/create_table.sql
+++ b/src/test/regress/sql/create_table.sql
@@ -526,11 +526,11 @@ CREATE TABLE part_b PARTITION OF parted (
CONSTRAINT check_b CHECK (b >= 0)
) FOR VALUES IN ('b');
-- conislocal should be false for any merged constraints, true otherwise
-SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY conislocal, coninhcount;
+SELECT conname, conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY coninhcount DESC, conname;
-- Once check_b is added to the parent, it should be made non-local for part_b
ALTER TABLE parted ADD CONSTRAINT check_b CHECK (b >= 0);
-SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass;
+SELECT conname, conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY coninhcount DESC, conname;
-- Neither check_a nor check_b are droppable from part_b
ALTER TABLE part_b DROP CONSTRAINT check_a;
@@ -540,7 +540,7 @@ ALTER TABLE part_b DROP CONSTRAINT check_b;
-- traditional inheritance where they will be left behind, because they would
-- be local constraints.
ALTER TABLE parted DROP CONSTRAINT check_a, DROP CONSTRAINT check_b;
-SELECT conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass;
+SELECT conname, conislocal, coninhcount FROM pg_constraint WHERE conrelid = 'part_b'::regclass ORDER BY coninhcount DESC, conname;
-- specify PARTITION BY for a partition
CREATE TABLE fail_part_col_not_found PARTITION OF parted FOR VALUES IN ('c') PARTITION BY RANGE (c);
diff --git a/src/test/regress/sql/indexing.sql b/src/test/regress/sql/indexing.sql
index c3473589bfd..44f6788915c 100644
--- a/src/test/regress/sql/indexing.sql
+++ b/src/test/regress/sql/indexing.sql
@@ -569,7 +569,7 @@ create table idxpart3 (b int not null, a int not null);
alter table idxpart attach partition idxpart3 for values from (20, 20) to (30, 30);
select conname, contype, conrelid::regclass, conindid::regclass, conkey
from pg_constraint where conrelid::regclass::text like 'idxpart%'
- order by conname;
+ order by conrelid::regclass::text, conname;
drop table idxpart;
-- Verify that multi-layer partitioning honors the requirement that all
@@ -667,9 +667,11 @@ create table idxpart (a int) partition by range (a);
create table idxpart0 (like idxpart);
alter table idxpart0 add unique (a);
alter table idxpart attach partition idxpart0 default;
-alter table only idxpart add primary key (a); -- fail, no NOT NULL constraint
+alter table only idxpart add primary key (a); -- works, but idxpart0.a is nullable
+\d idxpart0
+alter index idxpart_pkey attach partition idxpart0_a_key; -- fails, lacks NOT NULL
alter table idxpart0 alter column a set not null;
-alter table only idxpart add primary key (a); -- now it works
+alter index idxpart_pkey attach partition idxpart0_a_key;
alter table idxpart0 alter column a drop not null; -- fail, pkey needs it
drop table idxpart;
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index 0ce83f16ba7..4f269af166f 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -719,6 +719,189 @@ select * from cnullparent where f1 = 2;
drop table cnullparent cascade;
--
+-- Test inheritance of NOT NULL constraints
+--
+create table pp1 (f1 int);
+create table cc1 (f2 text, f3 int) inherits (pp1);
+\d cc1
+create table cc2(f4 float) inherits(pp1,cc1);
+\d cc2
+
+-- named NOT NULL constraint
+alter table cc1 add column a2 int constraint nn not null;
+\d+ cc1
+\d+ cc2
+alter table pp1 alter column f1 set not null;
+\d+ pp1
+\d+ cc1
+\d+ cc2
+
+-- remove constraint from cc2: no dice, it's inherited
+alter table cc2 alter column a2 drop not null;
+
+-- remove constraint cc1, should succeed
+alter table cc1 alter column a2 drop not null;
+\d+ cc1
+
+-- same for cc2
+alter table cc2 alter column f1 drop not null;
+\d+ cc2
+
+-- remove from cc1, should fail again
+alter table cc1 alter column f1 drop not null;
+
+-- remove from pp1, should succeed
+alter table pp1 alter column f1 drop not null;
+\d+ pp1
+
+alter table pp1 add primary key (f1);
+-- Leave these tables around, for pg_upgrade testing
+
+-- Test the same constraint name for different columns in different parents
+create table inh_parent1(a int constraint nn not null);
+create table inh_parent2(b int constraint nn not null);
+create table inh_child () inherits (inh_parent1, inh_parent2);
+\d+ inh_child
+drop table inh_parent1, inh_parent2, inh_child;
+
+-- Test multiple parents with overlapping primary keys
+create table inh_parent1(a int, b int, c int, primary key (a, b));
+create table inh_parent2(d int, e int, b int, primary key (d, b));
+create table inh_child() inherits (inh_parent1, inh_parent2);
+select conrelid::regclass, conname, contype, conkey,
+ coninhcount, conislocal, connoinherit
+ from pg_constraint where contype in ('n','p') and
+ conrelid::regclass::text in ('inh_child', 'inh_parent1', 'inh_parent2')
+ order by 1, 2;
+\d+ inh_child
+drop table inh_parent1, inh_parent2, inh_child;
+
+-- NOT NULL NO INHERIT
+create table inh_nn_parent(a int);
+create table inh_nn_child() inherits (inh_nn_parent);
+alter table inh_nn_parent add not null a no inherit;
+create table inh_nn_child2() inherits (inh_nn_parent);
+select conrelid::regclass, conname, contype, conkey,
+ (select attname from pg_attribute where attrelid = conrelid and attnum = conkey[1]),
+ coninhcount, conislocal, connoinherit
+ from pg_constraint where contype = 'n' and
+ conrelid::regclass::text like 'inh\_nn\_%'
+ order by 2, 1;
+\d+ inh_nn*
+drop table inh_nn_parent, inh_nn_child, inh_nn_child2;
+
+--
+-- test inherit/deinherit
+--
+create table inh_parent(f1 int);
+create table inh_child1(f1 int not null);
+create table inh_child2(f1 int);
+
+-- inh_child1 should have not null constraint
+alter table inh_child1 inherit inh_parent;
+
+-- should fail, missing NOT NULL constraint
+alter table inh_child2 inherit inh_child1;
+
+alter table inh_child2 alter column f1 set not null;
+alter table inh_child2 inherit inh_child1;
+
+-- add NOT NULL constraint recursively
+alter table inh_parent alter column f1 set not null;
+
+\d+ inh_parent
+\d+ inh_child1
+\d+ inh_child2
+
+select conrelid::regclass, conname, contype, coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid in ('inh_parent'::regclass, 'inh_child1'::regclass, 'inh_child2'::regclass)
+ order by 2, 1;
+
+--
+-- test deinherit procedure
+--
+
+-- deinherit inh_child1
+create table inh_grandchld () inherits (inh_child1);
+alter table inh_child1 no inherit inh_parent;
+\d+ inh_parent
+\d+ inh_child1
+\d+ inh_child2
+select conrelid::regclass, conname, contype, coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid::regclass::text in ('inh_parent', 'inh_child1', 'inh_child2', 'inh_grandchld')
+ order by 2, 1;
+drop table inh_parent, inh_child1, inh_child2, inh_grandchld;
+
+-- a PK in parent must have a not-null in child that it can mark inherited
+create table inh_parent (a int primary key);
+create table inh_child (a int primary key);
+alter table inh_child inherit inh_parent; -- nope
+alter table inh_child alter a set not null;
+alter table inh_child inherit inh_parent; -- now it works
+drop table inh_parent, inh_child;
+
+--
+-- test multi inheritance tree
+--
+create table inh_parent(f1 int not null);
+create table inh_child1() inherits(inh_parent);
+create table inh_child2() inherits(inh_parent);
+create table inh_grandchld() inherits(inh_child1, inh_child2);
+
+-- show constraint info
+select conrelid::regclass, conname, contype, coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid in ('inh_parent'::regclass, 'inh_child1'::regclass, 'inh_child2'::regclass, 'inh_grandchld'::regclass)
+ order by 2, conrelid::regclass::text;
+
+drop table inh_parent cascade;
+
+-- test child table with inherited columns and
+-- with explicitly specified not null constraints
+create table inh_parent_1(f1 int);
+create table inh_parent_2(f2 text);
+create table inh_child(f1 int not null, f2 text not null) inherits(inh_parent_1, inh_parent_2);
+
+-- show constraint info
+select conrelid::regclass, conname, contype, coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid in ('inh_parent_1'::regclass, 'inh_parent_2'::regclass, 'inh_child'::regclass)
+ order by 2, conrelid::regclass::text;
+
+-- also drops inh_child table
+drop table inh_parent_1 cascade;
+drop table inh_parent_2;
+
+-- test multi layer inheritance tree
+create table inh_p1(f1 int not null);
+create table inh_p2(f1 int not null);
+create table inh_p3(f2 int);
+create table inh_p4(f1 int not null, f3 text not null);
+
+create table inh_multiparent() inherits(inh_p1, inh_p2, inh_p3, inh_p4);
+
+-- constraint on f1 should have three parents
+select conrelid::regclass, contype, conname,
+ (select attname from pg_attribute where attrelid = conrelid and attnum = conkey[1]),
+ coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid::regclass in ('inh_p1', 'inh_p2', 'inh_p3', 'inh_p4',
+ 'inh_multiparent')
+ order by conrelid::regclass::text, conname;
+
+create table inh_multiparent2 (a int not null, f1 int) inherits(inh_p3, inh_multiparent);
+select conrelid::regclass, contype, conname,
+ (select attname from pg_attribute where attrelid = conrelid and attnum = conkey[1]),
+ coninhcount, conislocal
+ from pg_constraint where contype = 'n' and
+ conrelid::regclass in ('inh_p3', 'inh_multiparent', 'inh_multiparent2')
+ order by conrelid::regclass::text, conname;
+
+drop table inh_p1, inh_p2, inh_p3, inh_p4 cascade;
+
+--
-- Check use of temporary tables with inheritance trees
--
create table inh_perm_parent (a1 int);
diff --git a/src/test/regress/sql/replica_identity.sql b/src/test/regress/sql/replica_identity.sql
index 14620b77130..dd43650586c 100644
--- a/src/test/regress/sql/replica_identity.sql
+++ b/src/test/regress/sql/replica_identity.sql
@@ -97,6 +97,9 @@ ALTER TABLE test_replica_identity3 ALTER COLUMN id TYPE bigint;
-- ALTER TABLE DROP NOT NULL is not allowed for columns part of an index
-- used as replica identity.
ALTER TABLE test_replica_identity3 ALTER COLUMN id DROP NOT NULL;
+-- but it's OK when the identity is FULL
+ALTER TABLE test_replica_identity3 REPLICA IDENTITY FULL;
+ALTER TABLE test_replica_identity3 ALTER COLUMN id DROP NOT NULL;
--
-- Test that replica identity can be set on an index that's not yet valid.
@@ -117,8 +120,20 @@ ALTER INDEX test_replica_identity4_pkey
ATTACH PARTITION test_replica_identity4_1_pkey;
\d+ test_replica_identity4
+-- Dropping the primary key is not allowed if that would leave the replica
+-- identity as nullable
+CREATE TABLE test_replica_identity5 (a int not null, b int, c int,
+ PRIMARY KEY (b, c));
+CREATE UNIQUE INDEX test_replica_identity5_a_b_key ON test_replica_identity5 (a, b);
+ALTER TABLE test_replica_identity5 REPLICA IDENTITY USING INDEX test_replica_identity5_a_b_key;
+ALTER TABLE test_replica_identity5 DROP CONSTRAINT test_replica_identity5_pkey;
+ALTER TABLE test_replica_identity5 ALTER b SET NOT NULL;
+ALTER TABLE test_replica_identity5 DROP CONSTRAINT test_replica_identity5_pkey;
+ALTER TABLE test_replica_identity5 ALTER b DROP NOT NULL;
+
DROP TABLE test_replica_identity;
DROP TABLE test_replica_identity2;
DROP TABLE test_replica_identity3;
DROP TABLE test_replica_identity4;
+DROP TABLE test_replica_identity5;
DROP TABLE test_replica_identity_othertable;