Re: Eliminating SPI / SQL from some RI triggers - take 3
Chao Li <li.evan.chao@gmail.com>
From: Chao Li <li.evan.chao@gmail.com>
To: Amit Langote <amitlangote09@gmail.com>
Cc: Junwang Zhao <zhjwpku@gmail.com>,
Haibo Yan <tristan.yim@gmail.com>,
Pavel Stehule <pavel.stehule@gmail.com>,
PostgreSQL-development <pgsql-hackers@postgresql.org>,
Tomas Vondra <tomas@vondra.me>
Date: 2026-04-07T01:45:42Z
Lists: pgsql-hackers
> On Apr 6, 2026, at 17:45, Amit Langote <amitlangote09@gmail.com> wrote:
>
> On Fri, Apr 3, 2026 at 6:39 PM Amit Langote <amitlangote09@gmail.com> wrote:
>> On Fri, Apr 3, 2026 at 5:58 PM Chao Li <li.evan.chao@gmail.com> wrote:
>>>> On Apr 3, 2026, at 13:52, Amit Langote <amitlangote09@gmail.com> wrote:
>>>> On Thu, Apr 2, 2026 at 5:00 PM Chao Li <li.evan.chao@gmail.com> wrote:
>>>>> I plan to spend time testing and tracing this patch tomorrow. But I don’t want to block your progress, if I find anything, I will report to you.
>>>>
>>>> Sure, I didn't want to leave committing this to the weekend or the next week.
>>>
>>> I spent several hours debugging this patch today, and I found a problem where the batch mode doesn't seem to handle deferred RI triggers, although the commit message suggests that it should.
>>>
>>> I traced this scenario:
>>> ```
>>> CREATE TABLE pk (a int primary key);
>>> CREATE TABLE fk (a int references pk(a) DEFERRABLE INITIALLY DEFERRED);
>>> BEGIN;
>>> INSERT INTO fk VALUES (1);
>>> INSERT INTO pk VALUES (1);
>>> COMMIT;
>>> ```
>>>
>>> When COMMIT is executed, it reaches RI_FKey_check(), where AfterTriggerIsActive() checks whether afterTriggers.query_depth >= 0. But in the deferred case, afterTriggers.query_depth is -1.
>>>
>>> From the code:
>>> ```
>>> if (ri_fastpath_is_applicable(riinfo))
>>> {
>>> if (AfterTriggerIsActive())
>>> {
>>> /* Batched path: buffer and probe in groups */
>>> ri_FastPathBatchAdd(riinfo, fk_rel, newslot);
>>> }
>>> else
>>> {
>>> /* ALTER TABLE validation: per-row, no cache */
>>> ri_FastPathCheck(riinfo, fk_rel, newslot);
>>> }
>>> return PointerGetDatum(NULL);
>>> }
>>> ```
>>>
>>> So this ends up falling back to the per-row path for deferred RI checks at COMMIT, even though the intent here seems to be only to bypass the ALTER TABLE validation case, where batch callbacks would never fire, and MyTriggerDepth is 0. So, maybe we can just check MyTriggerDepth>0 in AfterTriggerIsActive().
>>>
>>> I tried the attached fix. With it, deferred triggers go through the batch mode, and all existing tests still pass.
>>
>> I think you might be right. Thanks for the patch. It looks correct
>> to me at a glance, but I will need to check it a bit more closely
>> before committing.
>
> Thinking about this some more, your fix is on the right track but
> needs a bit more work -- MyTriggerDepth > 0 is too broad since it
> fires for BEFORE triggers too. I have a revised version using a new
> afterTriggerFiringDepth counter that I'll push shortly.
>
> Added an open item for tracking in the meantime:
> https://wiki.postgresql.org/wiki/PostgreSQL_19_Open_Items#Open_Issues
>
> --
> Thanks, Amit Langote
> <v2-0001-Fix-deferred-FK-check-batching-introduced-by-comm.patch>
V2 looks good to me. Besides the normal cases, I also traced an abnormal case to verify that afterTriggerFiringDepth is always reset to 0:
```
evantest=# begin;
BEGIN
evantest=*# INSERT INTO fk VALUES (2);
INSERT 0 1
evantest=*# commit;
ERROR: insert or update on table "fk" violates foreign key constraint "fk_a_fkey"
DETAIL: Key (a)=(2) is not present in table "pk".
```
Best regards,
--
Chao Li (Evan)
HighGo Software Co., Ltd.
https://www.highgo.com/
Commits
Same data as JSON:
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Fix some const qualifier use in ri_triggers.c
- 1b9dc2cb7574 19 (unreleased) landed
-
doc: Improve consistency of parallel vacuum description.
- 2a3d2f9f68da 19 (unreleased) cited
-
Move afterTriggerFiringDepth into AfterTriggersData
- d6e96bacd3c0 19 (unreleased) landed
-
Fix typo left by 34a30786293
- 03029409b409 19 (unreleased) landed
-
Fix RI fast-path crash under nested C-level SPI
- 34a307862930 19 (unreleased) landed
-
Add nkeys parameter to recheck_matched_pk_tuple()
- e1cc57fabd6c 19 (unreleased) landed
-
Fix deferred FK check batching introduced by commit b7b27eb41a5
- 5c54c3ed1b93 19 (unreleased) landed
-
Optimize fast-path FK checks with batched index probes
- b7b27eb41a5c 19 (unreleased) landed
-
Make FastPathMeta self-contained by copying FmgrInfo structs
- edee5634564d 19 (unreleased) landed
-
Add fast path for foreign key constraint checks
- 2da86c1ef9b5 19 (unreleased) landed