On 2025-Sep-04, jian he wrote:

> @@ -3093,6 +3115,16 @@ AddRelationNotNullConstraints(Relation rel, List 
> *constraints,
>                                       conname = other->name;
>  
>                               inhcount++;
> +
> +                             /*
> +                              * if a column inherit multiple not-null 
> constraints, the
> +                              * enforced status should the same.
> +                             */
> +                             if (other->is_enforced != cooked->is_enforced)
> +                                     ereport(ERROR,
> +                                                     
> errcode(ERRCODE_DATATYPE_MISMATCH),
> +                                                     errmsg("cannot define 
> not-null constraint on column \"%s\"", conname),
> +                                                     errdetail("The column 
> inherited not-null constraints have conflict ENFORCED status."));
>                               old_notnulls = 
> list_delete_nth_cell(old_notnulls, restpos);
>                       }
>                       else

Hmmm, are you sure about this?   I think if a table has two parents, one
with enforced and the other with not enforced constraint, then it's okay
to get them combined resulting in one enforced constraint.

> @@ -777,6 +778,18 @@ AdjustNotNullInheritance(Oid relid, AttrNumber attnum,
>                                       errhint("You might need to validate it 
> using %s.",
>                                                       "ALTER TABLE ... 
> VALIDATE CONSTRAINT"));
>  
> +             /*
> +              * If the ENFORCED status we're asked for doesn't match what the
> +              * existing constraint has, throw an error.
> +              */
> +             if (is_enforced != conform->conenforced)
> +                     ereport(ERROR,
> +                                     
> errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
> +                                     errmsg("cannot change ENFORCED status 
> of NOT NULL constraint \"%s\" on relation \"%s\"",
> +                                                NameStr(conform->conname), 
> get_rel_name(relid)),
> +                                     errhint("You might need to drop the 
> existing not enforced constraint using %s.",
> +                                                     "ALTER TABLE ... DROP 
> CONSTRAINT"));

I think the hint here should suggest to make the existing constraint as
enforced, rather than drop it.

> @@ -9937,9 +9962,9 @@ ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo 
> *tab, Relation rel,
>                * If adding a valid not-null constraint, set the pg_attribute 
> flag
>                * and tell phase 3 to verify existing rows, if needed.  For an
>                * invalid constraint, just set attnotnull, without queueing
> -              * verification.
> +              * verification. For not enforced not-null, no need set 
> attnotnull.
>                */
> -             if (constr->contype == CONSTR_NOTNULL)
> +             if (constr->contype == CONSTR_NOTNULL && ccon->is_enforced)
>                       set_attnotnull(wqueue, rel, ccon->attnum,
>                                                  !constr->skip_validation,
>                                                  !constr->skip_validation);

Didn't we decide that attnotnull meant whether a constraint exists at
all, without making a judgement on whether it's enforced or valid?  The
important change should be in CheckNNConstraintFetch() which should
determine attnullability in a way that allows executor know whether the
column is nullable or not.  I admit we might want to handle this
differently for unenforced constraints, but we should discuss that and
make sure that's what we want.

> +             else if (notenforced)
> +             {
> +                     /*
> +                      * We can't use ATExecSetNotNull here because it adds 
> an enforced
> +                      * not-null constraint, but here we only want a 
> non-enforced one.
> +                     */

Umm, wouldn't it make more sense to modify ATExecSetNotNull() so that it
does what we want?  This seems hackish.

> @@ -1272,33 +1294,41 @@ transformTableLikeClause(CreateStmtContext *cxt, 
> TableLikeClause *table_like_cla
>        * Reproduce not-null constraints, if any, by copying them.  We do this
>        * regardless of options given.
>        */
> -     if (tupleDesc->constr && tupleDesc->constr->has_not_null)
> -     {
> -             List       *lst;
> +     lst = RelationGetNotNullConstraints(RelationGetRelid(relation), false,
> +                                                                             
> true);
> +     cxt->nnconstraints = list_concat(cxt->nnconstraints, lst);
>  
> -             lst = RelationGetNotNullConstraints(RelationGetRelid(relation), 
> false,
> -                                                                             
>         true);

> +     /*
> +      * When creating a new relation, marking the enforced not-null 
> constraint as
> +      * not valid doesn't make sense, so we treat it as valid.
> +     */
> +     foreach_node(Constraint, nnconstr, lst)
> +     {
> +             if (nnconstr->is_enforced)
> +             {
> +                     nnconstr->skip_validation = false;
> +                     nnconstr->initially_valid = true;
> +             }
> +     }

Hmmm, this bit here (making constraints as valid if they're not valid in
the source table) looks like a fix for the existing code.  I think it
should be a separate patch, perhaps back-patchable to 18.  Or maybe I'm
missing something ...?

-- 
Álvaro Herrera               48°01'N 7°57'E  —  https://www.EnterpriseDB.com/
"Digital and video cameras have this adjustment and film cameras don't for the
same reason dogs and cats lick themselves: because they can."   (Ken Rockwell)


Reply via email to