Re: [PATCH] Fix unexpected loss of DEFERRABLE property after toggling NOT ENFORCED / ENFORCED

From: Yasuo Honda <yasuo(dot)honda(at)gmail(dot)com>
To: Fujii Masao <masao(dot)fujii(at)gmail(dot)com>
Cc: PostgreSQL-development <pgsql-hackers(at)postgresql(dot)org>
Subject: Re: [PATCH] Fix unexpected loss of DEFERRABLE property after toggling NOT ENFORCED / ENFORCED
Date: 2026-03-27 07:41:36
Message-ID: CAKmOUTkrgZCWq63Ab0Ff01JQ7u3ua__NyT=V8mZxcH3sSAhX3w@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

(Resending to the list, as my previous reply went to the sender only.)

Thanks for the comment.

> Regarding the regression test, would it be better to verify not only catalog
state (e.g., pg_trigger) but also the actual behavior?

Agreed. In addition to inspecting pg_trigger contents, I've added test
cases that verify the actual runtime behavior of deferred constraints.
I've also covered INITIALLY IMMEDIATE alongside INITIALLY DEFERRED.

--
Yasuo Honda

On Tue, Mar 24, 2026 at 3:30 PM Fujii Masao <masao(dot)fujii(at)gmail(dot)com> wrote:
>
> On Fri, Feb 27, 2026 at 6:51 PM Yasuo Honda <yasuo(dot)honda(at)gmail(dot)com> wrote:
> >
> > Hi,
> >
> > I have encountered an unexpected behavior where the DEFERRABLE and
> > INITIALLY DEFERRED properties of foreign keys are lost after toggling
> > them from NOT ENFORCED to ENFORCED.
> >
> > Background
> >
> > In the Ruby on Rails framework, there is a built-in mechanism to
> > temporarily bypass foreign key checks while loading test data.
> > Currently, this is implemented using: ALTER TABLE ... DISABLE TRIGGER
> > ALL; ALTER TABLE ... ENABLE TRIGGER ALL;
> >
> > However, this requires superuser privileges. With the newly introduced
> > support for "NOT ENFORCED" foreign keys in PostgreSQL 18, I am
> > interested in switching to: ALTER TABLE ... ALTER CONSTRAINT ... NOT
> > ENFORCED; ALTER TABLE ... ALTER CONSTRAINT ... ENFORCED;
> >
> > This would allow the operation to be performed by the table owner
> > without superuser rights. However, I discovered that switching the
> > state back to ENFORCED unexpectedly strips away the DEFERRABLE
> > property.
> >
> > Problem
> >
> > When re-enforcing a constraint, there is a discrepancy between the
> > constraint definition and its underlying triggers.
> > While the flags in pg_constraint remain correct, the corresponding
> > triggers in pg_trigger (tgdeferrable and tginitdeferred) are reset to
> > defaults ('f') when they are reconstructed during the ENFORCED
> > operation.
> >
> > I have attached a reproduction SQL script that demonstrates this by
> > comparing the values in pg_constraint and pg_trigger. In the current
> > PostgreSQL 18.3, you can see that the triggers lose their
> > deferrability even though the constraint itself is still defined as
> > DEFERRABLE. This causes "SET CONSTRAINTS ... DEFERRED" to fail.
> >
> > After Patch
> >
> > * The tgdeferrable and tginitdeferred flags in pg_trigger are
> > correctly preserved to match pg_constraint after the toggle, and
> > deferred execution works as expected.
> >
> > I've attached the reproduction SQL script and a patch to fix this in
> > src/backend/commands/tablecmds.c. The patch and reproduction SQL
> > script were developed with the assistance of Claude Code. I have
> > reviewed and verified the code myself.
> >
> > Any feedback is appreciated.
>
> Thanks for reporting the issue and providing a patch!
>
> I was able to reproduce the issue on the master.
>
> The patch looks good overall, but since I'm not very familiar with this area,
> I'd like to spend a bit more time reviewing the changes in detail.
>
> Regarding the regression test, would it be better to verify not only catalog
> state (e.g., pg_trigger) but also the actual behavior? For example, we could
> check that a foreign key violation is not raised immediately on INSERT,
> but instead at COMMIT even after ALTER CONSTRAINT ENFORCED:
>
> ALTER TABLE ... ALTER CONSTRAINT ... NOT ENFORCED;
> ALTER TABLE ... ALTER CONSTRAINT ... ENFORCED;
> BEGIN;
> INSERT INTO t VALUES (1);
> COMMIT;
>
> In this case, the foreign key violation should be reported at COMMIT,
> even after ALTER CONSTRAINT ... ENFORCED.
>
> Regards,
>
> --
> Fujii Masao

Attachment Content-Type Size
v2-Restore-tgdeferrable-and-tginitdeferred-after-NOT.patch application/octet-stream 6.7 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Yugo Nagata 2026-03-27 07:43:59 Re: Allow to collect statistics on virtual generated columns
Previous Message Peter Eisentraut 2026-03-27 07:38:45 Re: Enable -Wstrict-prototypes and -Wold-style-definition by default