Fix not-null constraint test

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
This commit is contained in:
Alvaro Herrera 2023-09-01 19:49:20 +02:00
parent e09d763e25
commit d0ec2ddbe0
No known key found for this signature in database
GPG Key ID: 1C20ACB9D5C564AE
3 changed files with 41 additions and 7 deletions

View File

@ -15746,9 +15746,10 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
attributeName))); attributeName)));
/* /*
* Check child doesn't discard NOT NULL property. (Other * If the parent has a not-null constraint that's not NO INHERIT,
* constraints are checked elsewhere.) However, if the constraint * make sure the child has one too.
* is NO INHERIT in the parent, this is allowed. *
* Other constraints are checked elsewhere.
*/ */
if (attribute->attnotnull && !childatt->attnotnull) if (attribute->attnotnull && !childatt->attnotnull)
{ {
@ -15756,11 +15757,12 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel), contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
attribute->attnum); attribute->attnum);
if (!((Form_pg_constraint) GETSTRUCT(contup))->connoinherit) if (HeapTupleIsValid(contup) &&
!((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("column \"%s\" in child table must be marked NOT NULL", errmsg("column \"%s\" in child table must be marked NOT NULL",
attributeName))); attributeName));
} }
/* /*
@ -15981,10 +15983,20 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
systable_endscan(child_scan); systable_endscan(child_scan);
if (!found) 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, ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH), (errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("child table is missing constraint \"%s\"", errmsg("child table is missing constraint \"%s\"",
NameStr(parent_con->conname)))); NameStr(parent_con->conname))));
}
} }
systable_endscan(parent_scan); systable_endscan(parent_scan);

View File

@ -1006,6 +1006,17 @@ Inherits: cnn_grandchild,
ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey; ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
ERROR: constraint "cnn_parent_pkey" of relation "cnn_parent" does not exist ERROR: constraint "cnn_parent_pkey" of relation "cnn_parent" does not exist
-- keeps these tables around, for pg_upgrade testing -- 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 -- Comments
-- Setup a low-level role to enforce non-superuser checks. -- Setup a low-level role to enforce non-superuser checks.
CREATE ROLE regress_constraint_comments; CREATE ROLE regress_constraint_comments;

View 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; ALTER TABLE cnn_parent DROP CONSTRAINT cnn_parent_pkey;
-- keeps these tables around, for pg_upgrade testing -- 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 -- Comments
-- Setup a low-level role to enforce non-superuser checks. -- Setup a low-level role to enforce non-superuser checks.