Conflicting constraint being merged

From: Amit Langote <Langote_Amit_f8(at)lab(dot)ntt(dot)co(dot)jp>
To: Pg Hackers <pgsql-hackers(at)postgresql(dot)org>
Subject: Conflicting constraint being merged
Date: 2016-10-12 07:55:46
Message-ID: b28ee774-7009-313d-dd55-5bdd81242c41@lab.ntt.co.jp
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Currently, if child table has a non-inherited constraint and a constraint
with the same name is added to the parent, it will fail with an error as
illustrated below:

create table parent (a int);
CREATE TABLE
create table child (constraint check_a check (a > 0) no inherit) inherits
(parent);
CREATE TABLE
alter table parent add constraint check_a check (a > 0);
ERROR: constraint "check_a" conflicts with non-inherited constraint on
relation "child"

Or if parent with a inheritable constraint is added to inheritance parents
of child with non-inherited constraint of the same name:

create table parent (a int, constraint check_a check (a > 0));
CREATE TABLE
create table child (a int, constraint check_a check (a > 0) no inherit)
CREATE TABLE
alter table child inherit parent;
ERROR: constraint "check_a" conflicts with non-inherited constraint on
child table "child"

If we had allowed it to be merged, any children of child itself won't
inherit that constraint (because on child it's marked NO INHERIT), which
would not be good.

However, it is still possible for a child to override/hide the parent's
constraint as follows:

create table parent (a int, constraint check_a check (a > 0));
CREATE TABLE
create table child (constraint check_a check (a > 0) no inherit) inherits
(parent);
CREATE TABLE

Note that child's "no inherit" constraint check_a has been successfully
created. If we create its child table, the same won't be inherited.

create table child_child (a int) inherits (child);
CREATE TABLE

At this point:

\d parent
Table "public.parent"
Column | Type | Modifiers
--------+---------+-----------
a | integer |
Check constraints:
"check_a" CHECK (a > 0)
Number of child tables: 1 (Use \d+ to list them.)

\d child
Table "public.child"
Column | Type | Modifiers
--------+---------+-----------
a | integer |
Check constraints:
"check_a" CHECK (a > 0) NO INHERIT
Inherits: parent
Number of child tables: 1 (Use \d+ to list them.)

\d child_child
Table "public.child_child"
Column | Type | Modifiers
--------+---------+-----------
a | integer |
Inherits: child

Inserting 0 into child_child is fine, whereas it's not fine in parent or
child.

insert into child_child values (0);
INSERT 0 1

Selecting from parent will now return some rows violating one of its valid
constraints viz. check (a > 0).

select * from parent;
a
---
0
(1 row)

I am afraid this is an oversight/bug. Attached tries to fix the same - In
MergeWithExistingConstraint(), we check if the new "no inherit" constraint
being added to a relation matches one of its existing constraints that is
*inherited*. So the following will now happen:

create table parent (a int, constraint check_a check (a > 0));
CREATE TABLE
create table child (constraint check_a check (a > 0) no inherit) inherits
(parent);
ERROR: constraint "check_a" conflicts with inherited constraint on
relation "child"

Thoughts?

Thanks,
Amit

Attachment Content-Type Size
no-merge-noinh-con-with-inh-con-1.patch text/x-diff 2.0 KB

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Ernst-Georg Schmid 2016-10-12 07:58:39 Re: How to inspect tuples during execution of a plan?
Previous Message Craig Ringer 2016-10-12 07:32:34 Re: macaddr 64 bit (EUI-64) datatype support