> On Apr 6, 2026, at 17:45, Amit Langote <[email protected]> wrote:
> 
> On Fri, Apr 3, 2026 at 6:39 PM Amit Langote <[email protected]> wrote:
>> On Fri, Apr 3, 2026 at 5:58 PM Chao Li <[email protected]> wrote:
>>>> On Apr 3, 2026, at 13:52, Amit Langote <[email protected]> wrote:
>>>> On Thu, Apr 2, 2026 at 5:00 PM Chao Li <[email protected]> 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/






Reply via email to