pg.ddx.io pgsql-committers@postgresql.org mailing list archivehelp / color / mirror / Atom feed
pgsql: Restore after-trigger firing context at subtransaction end 2+ messages / 1 participants [nested] [flat]
* pgsql: Restore after-trigger firing context at subtransaction end @ 2026-08-20 05:03 Amit Langote <amitlan@postgresql.org> 0 siblings, 0 replies; 2+ messages in thread From: Amit Langote @ 2026-08-20 05:03 UTC (permalink / raw) To: pgsql-committers@lists.postgresql.org Restore after-trigger firing context at subtransaction end AfterTriggerEndQuery(), AfterTriggerFireDeferred(), and AfterTriggerSetState() bracket their firing loops with firing_depth++/--. The decrement runs after the loop and is not protected by PG_FINALLY, so an error caught by a subtransaction (e.g. a PL/pgSQL EXCEPTION block) leaves firing_depth too high. Separately, AfterTriggerEndSubXact() unconditionally cleared firing_batch_callbacks, even if the subtransaction began while an outer batch-callback loop was active. firing_depth feeds AfterTriggerIsActive(), which the RI fast path uses to decide whether an FK check is running inside trigger firing and may batch. A stranded firing_depth makes AfterTriggerIsActive() wrongly report firing as active afterwards. This is reachable and results in silent data corruption: after a caught FK-check error, an ALTER TABLE ... ADD FOREIGN KEY whose validation runs per-row (RI_Initial_Check() having bailed, e.g. because RLS is enabled on the referenced table) calls RI_FKey_check() with AfterTriggerIsActive() wrongly true. The check is routed into the batched fast path, but a utility command has no AfterTriggerEndQuery() to fire the flush callback. The violating row is not reported, the constraint is marked validated, and the cached PK relation and index leak. Save firing_depth and firing_batch_callbacks at subtransaction start and restore them in AfterTriggerEndSubXact(), next to the existing query_depth handling. Restoring, rather than zeroing or clearing, is required because a subtransaction can begin and end while an outer query is firing, where firing_depth is legitimately positive and firing_batch_callbacks may be legitimately set. Reported-by: Noah Misch <noah@leadboat.com> Discussion: https://postgr.es/m/20260705222115.be.noahmisch@microsoft.com Backpatch-through: 19 Branch ------ REL_19_STABLE Details ------- https://git.postgresql.org/pg/commitdiff/f3a52a229adeb9c54e5b656b45af609b967874d6 Modified Files -------------- src/backend/commands/trigger.c | 29 ++++++++++++++++++++-- src/test/regress/expected/foreign_key.out | 41 +++++++++++++++++++++++++++++++ src/test/regress/sql/foreign_key.sql | 40 ++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) ^ permalink raw reply [nested|flat] 2+ messages in thread
* pgsql: Restore after-trigger firing context at subtransaction end @ 2026-08-20 05:04 Amit Langote <amitlan@postgresql.org> 0 siblings, 0 replies; 2+ messages in thread From: Amit Langote @ 2026-08-20 05:04 UTC (permalink / raw) To: pgsql-committers@lists.postgresql.org Restore after-trigger firing context at subtransaction end AfterTriggerEndQuery(), AfterTriggerFireDeferred(), and AfterTriggerSetState() bracket their firing loops with firing_depth++/--. The decrement runs after the loop and is not protected by PG_FINALLY, so an error caught by a subtransaction (e.g. a PL/pgSQL EXCEPTION block) leaves firing_depth too high. Separately, AfterTriggerEndSubXact() unconditionally cleared firing_batch_callbacks, even if the subtransaction began while an outer batch-callback loop was active. firing_depth feeds AfterTriggerIsActive(), which the RI fast path uses to decide whether an FK check is running inside trigger firing and may batch. A stranded firing_depth makes AfterTriggerIsActive() wrongly report firing as active afterwards. This is reachable and results in silent data corruption: after a caught FK-check error, an ALTER TABLE ... ADD FOREIGN KEY whose validation runs per-row (RI_Initial_Check() having bailed, e.g. because RLS is enabled on the referenced table) calls RI_FKey_check() with AfterTriggerIsActive() wrongly true. The check is routed into the batched fast path, but a utility command has no AfterTriggerEndQuery() to fire the flush callback. The violating row is not reported, the constraint is marked validated, and the cached PK relation and index leak. Save firing_depth and firing_batch_callbacks at subtransaction start and restore them in AfterTriggerEndSubXact(), next to the existing query_depth handling. Restoring, rather than zeroing or clearing, is required because a subtransaction can begin and end while an outer query is firing, where firing_depth is legitimately positive and firing_batch_callbacks may be legitimately set. Reported-by: Noah Misch <noah@leadboat.com> Discussion: https://postgr.es/m/20260705222115.be.noahmisch@microsoft.com Backpatch-through: 19 Branch ------ master Details ------- https://git.postgresql.org/pg/commitdiff/785dd853b6ccd4556de34a5fc5c984cff233e737 Modified Files -------------- src/backend/commands/trigger.c | 29 ++++++++++++++++++++-- src/test/regress/expected/foreign_key.out | 41 +++++++++++++++++++++++++++++++ src/test/regress/sql/foreign_key.sql | 40 ++++++++++++++++++++++++++++++ 3 files changed, 108 insertions(+), 2 deletions(-) ^ permalink raw reply [nested|flat] 2+ messages in thread
end of thread, other threads:[~2026-08-20 05:04 UTC | newest] Thread overview: 2+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2026-08-20 05:03 pgsql: Restore after-trigger firing context at subtransaction end Amit Langote <amitlan@postgresql.org> 2026-08-20 05:04 pgsql: Restore after-trigger firing context at subtransaction end Amit Langote <amitlan@postgresql.org>
This inbox is served by DDX for PostgreSQL; see mirroring instructions for how to clone and mirror all data and code used for this inbox