Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Fix not-null constraint test
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 1 Sep 2023 17:49:20 +0000 (19:49 +0200)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Fri, 1 Sep 2023 17:49:20 +0000 (19:49 +0200)
When a partitioned table has a primary key, trying to find the
corresponding not-null constraint for that column would come up empty,
causing code that's trying to check said not-null constraint to crash.
Fix by only running the check when the not-null constraint exists.

Reported-by: Alexander Lakhin <exclusion@gmail.com>
Discussion: https://postgr.es/m/d57b4a69-7394-3146-5976-9a1ef27e7972@gmail.com

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

index 03397746724d1d6cccce73679556755ac44fe6c7..47c556669f33e73dd1a6e01505886b1cb0872f01 100644 (file)
@@ -15746,9 +15746,10 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
                                attributeName)));
 
            /*
-            * Check child doesn't discard NOT NULL property.  (Other
-            * constraints are checked elsewhere.)  However, if the constraint
-            * is NO INHERIT in the parent, this is allowed.
+            * If the parent has a not-null constraint that's not NO INHERIT,
+            * make sure the child has one too.
+            *
+            * Other constraints are checked elsewhere.
             */
            if (attribute->attnotnull && !childatt->attnotnull)
            {
@@ -15756,11 +15757,12 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
 
                contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
                                                     attribute->attnum);
-               if (!((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
+               if (HeapTupleIsValid(contup) &&
+                   !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
                    ereport(ERROR,
-                           (errcode(ERRCODE_DATATYPE_MISMATCH),
-                            errmsg("column \"%s\" in child table must be marked NOT NULL",
-                                   attributeName)));
+                           errcode(ERRCODE_DATATYPE_MISMATCH),
+                           errmsg("column \"%s\" in child table must be marked NOT NULL",
+                                  attributeName));
            }
 
            /*
@@ -15981,10 +15983,20 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
        systable_endscan(child_scan);
 
        if (!found)
+       {
+           if (parent_con->contype == CONSTRAINT_NOTNULL)
+               ereport(ERROR,
+                       errcode(ERRCODE_DATATYPE_MISMATCH),
+                       errmsg("column \"%s\" in child table must be marked NOT NULL",
+                              get_attname(parent_relid,
+                                          extractNotNullColumn(parent_tuple),
+                                          false)));
+
            ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                     errmsg("child table is missing constraint \"%s\"",
                            NameStr(parent_con->conname))));
+       }
    }
 
    systable_endscan(parent_scan);
index b7de50ad6a6077406eeee32118e55c7ba45ffc1a..5b068477bf8f1ca98c798f47837d581f0718a894 100644 (file)
@@ -1006,6 +1006,17 @@ Inherits: cnn_grandchild,
 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
+-- ensure columns in partitions are marked not-null
+create table cnn2_parted(a int primary key) partition by list (a);
+create table cnn2_part1(a int);
+alter table cnn2_parted attach partition cnn2_part1 for values in (1);
+ERROR:  primary key column "a" is not marked NOT NULL
+drop table cnn2_parted, cnn2_part1;
+create table cnn2_parted(a int not null) partition by list (a);
+create table cnn2_part1(a int primary key);
+alter table cnn2_parted attach partition cnn2_part1 for values in (1);
+ERROR:  column "a" in child table must be marked NOT NULL
+drop table cnn2_parted, cnn2_part1;
 -- Comments
 -- Setup a low-level role to enforce non-superuser checks.
 CREATE ROLE regress_constraint_comments;
index 782699a43775bf7a9059155dbf66f90e869c8c06..a7d96e98f5889d9e8235ca07a1789bae5b78a0ff 100644 (file)
@@ -657,6 +657,17 @@ ALTER TABLE cnn_parent ADD PRIMARY KEY USING INDEX b_uq;
 ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
 -- keeps these tables around, for pg_upgrade testing
 
+-- ensure columns in partitions are marked not-null
+create table cnn2_parted(a int primary key) partition by list (a);
+create table cnn2_part1(a int);
+alter table cnn2_parted attach partition cnn2_part1 for values in (1);
+drop table cnn2_parted, cnn2_part1;
+
+create table cnn2_parted(a int not null) partition by list (a);
+create table cnn2_part1(a int primary key);
+alter table cnn2_parted attach partition cnn2_part1 for values in (1);
+drop table cnn2_parted, cnn2_part1;
+
 
 -- Comments
 -- Setup a low-level role to enforce non-superuser checks.