On Wed, Nov 13, 2024 at 10:49 PM Alvaro Herrera <alvhe...@alvh.no-ip.org> wrote:
> On 2024-Nov-13, Amit Langote wrote:
>
> > I rebased my patch over 14e87ffa5c54 and noticed that NOT NULL
> > constraints can also (or not) be marked NO INHERIT.  I think we should
> > allow NO INHERIT NOT NULL constraints on leaf partitions just like
> > CHECK constraints, so changed AddRelationNotNullConstraints() that way
> > and added some tests.
>
> OK, looks good.

Thanks.  I'll hold off pushing until we have some clarity on whether
the behavior should be identical for CHECK and NOT NULL constraints.

For now, I have removed the changes in my patch pertaining to NOT NULL
constraints.

> > However, I noticed that there is no clean way to do that for the following 
> > case:
> >
> > ALTER TABLE leaf_partition ADD CONSTRAINT col_nn_ni NOT NULL col NO INHERIT;
>
> Sorry, I didn't understand what's the initial state.  Does the
> constraint already exist here or not?

Sorry, here's the full example.  Note I'd changed both
AddRelationNotNullConstraints() and AdjustNotNullInheritance() to not
throw an error *if* the table is a leaf partition when the NO INHERIT
of an existing constraint doesn't match that of the new constraint.

create table p (a int not null) partition by list (a);
create table p1 partition of p for values in (1);
alter table p1 add constraint a_nn_ni not null a no inherit;

\d+ p1
                                           Table "public.p1"
 Column |  Type   | Collation | Nullable | Default | Storage |
Compression | Stats target | Description
--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
 a      | integer |           | not null |         | plain   |
    |              |
Partition of: p FOR VALUES IN (1)
Partition constraint: ((a IS NOT NULL) AND (a = 1))
Not-null constraints:
    "p_a_not_null" NOT NULL "a" (local, inherited)
Access method: heap

-- noop
alter table p1 add constraint a_nn_ni not null a no inherit;
alter table p1 add constraint a_nn_ni not null a no inherit;

\d+ p1
                                           Table "public.p1"
 Column |  Type   | Collation | Nullable | Default | Storage |
Compression | Stats target | Description
--------+---------+-----------+----------+---------+---------+-------------+--------------+-------------
 a      | integer |           | not null |         | plain   |
    |              |
Partition of: p FOR VALUES IN (1)
Partition constraint: ((a IS NOT NULL) AND (a = 1))
Not-null constraints:
    "p_a_not_null" NOT NULL "a" (local, inherited)
Access method: heap

The state of the existing inherited constraint is not valid and the
a_nn_ni is never created.

So, it seems that allowing NO INHERIT NOT NULL constraints of leaf
partitions to be merged with the inherited one is not as
straightforward as it is for CHECK constraints.

> > If I change the new function AdjustNotNullInheritance() added by your
> > commit to not throw an error for leaf partitions, the above constraint
> > (col_nn_ni) is not stored at all, because the function returns true in
> > that case, which means the caller does nothing with the constraint.  I
> > am not sure what the right thing to do would be.  If we return false,
> > the caller will store the constraint the first time around, but
> > repeating the command will again do nothing, not even prevent the
> > addition of a duplicate constraint.
>
> Do you mean if we return false, it allows two not-null constraints for
> the same column?  That would absolutely be a bug.

I don't think there is any such bug at the moment, because if
AdjustNotNullInheritance() finds an existing constraint, it always
returns true provided the new constraint does not cause the error due
to its NO INHERIT property not matching the existing one's.

> I think:
> * if a leaf partition already has an inherited not-null constraint
>   from its parent and we want to add another one, we should:
>   - if the one being added is NO INHERIT, then throw an error, because
>     we cannot merge them

Hmm, we'll be doing something else for CHECK constraints if we apply my patch:

create table p (a int not null, constraint check_a check (a > 0))
partition by list (a);
create table p1 partition of p (constraint check_a check (a > 0) no
inherit) for values in (1);
NOTICE:  merging constraint "check_a" with inherited definition

\d p1
                 Table "public.p1"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 a      | integer |           | not null |
Partition of: p FOR VALUES IN (1)
Check constraints:
    "check_a" CHECK (a > 0) NO INHERIT

I thought we were fine with allowing merging of such child
constraints, because leaf partitions can't have children to pass them
onto, so the NO INHERIT clause is essentially pointless.

>   - if the one being added is not NO INHERIT, then both constraints
>     would have the same state and so we silently do nothing.

Maybe we should emit some kind of NOTICE message in such cases?

> * if a leaf partition has a locally defined not-null marked NO INHERIT
>   - if we add another NO INHERIT, silently do nothing
>   - if we add an INHERIT one, throw an error because cannot merge.

So IIUC, there cannot be multiple *named* NOT NULL constraints for the
same column?

> If you want, you could leave the not-null constraint case alone and I
> can have a look later.  It wasn't my intention to burden you with that.

No worries.  I want to ensure that the behaviors for NOT NULL and
CHECK constraints are as consistent as possible.

Anyway, for now, I've fixed my patch to only consider CHECK
constraints -- leaf partitions can have inherited CHECK constraints
that are marked NO INHERIT.

--
Thanks, Amit Langote

Attachment: v3-0001-Allow-inherited-constraints-to-be-marked-NO-INHER.patch
Description: Binary data

Reply via email to