From 4ed90a5b74c77c5e4a2f5b0a602a06a372aec795 Mon Sep 17 00:00:00 2001
From: Amit Langote <amitlan@postgresql.org>
Date: Wed, 8 Apr 2026 23:22:41 +0900
Subject: [PATCH v3 3/4] Move afterTriggerFiringDepth into AfterTriggersData

The static variable afterTriggerFiringDepth is logically part of the
after-trigger state.  Move it into AfterTriggersData as firing_depth,
alongside query_depth and the other per-transaction after-trigger
state.
---
 src/backend/commands/trigger.c | 42 ++++++++++++++++++----------------
 1 file changed, 22 insertions(+), 20 deletions(-)

diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index f59537fe86e..bbc2405cc4a 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -3894,7 +3894,16 @@ typedef struct AfterTriggersData
 	AfterTriggersTransData *trans_stack;	/* array of structs shown below */
 	int			maxtransdepth;	/* allocated len of above array */
 
-	List	   *batch_callbacks;	/* List of AfterTriggerCallbackItem */
+	/*
+	 * Incremented around the trigger-firing loops in AfterTriggerEndQuery,
+	 * AfterTriggerFireDeferred, and AfterTriggerSetState.  Used by
+	 * AfterTriggerIsActive() and to tag batch callbacks with the depth at
+	 * which they should fire.
+	 */
+	int			firing_depth;
+
+	List	   *batch_callbacks;	/* List of AfterTriggerCallbackItem,
+									 * possibly from multiple firing depths */
 } AfterTriggersData;
 
 struct AfterTriggersQueryData
@@ -3942,13 +3951,6 @@ typedef struct AfterTriggerCallbackItem
 
 static AfterTriggersData afterTriggers;
 
-/*
- * Incremented before invoking afterTriggerInvokeEvents().  Used by
- * AfterTriggerIsActive() to determine whether batch callbacks will fire,
- * so that RI trigger functions can take the batched fast path.
- */
-static int	afterTriggerFiringDepth = 0;
-
 static void AfterTriggerExecute(EState *estate,
 								AfterTriggerEvent event,
 								ResultRelInfo *relInfo,
@@ -5108,6 +5110,7 @@ AfterTriggerBeginXact(void)
 	 */
 	afterTriggers.firing_counter = (CommandId) 1;	/* mustn't be 0 */
 	afterTriggers.query_depth = -1;
+	afterTriggers.firing_depth = 0;
 	afterTriggers.batch_callbacks = NIL;
 
 	/*
@@ -5122,7 +5125,6 @@ AfterTriggerBeginXact(void)
 	Assert(afterTriggers.events.head == NULL);
 	Assert(afterTriggers.trans_stack == NULL);
 	Assert(afterTriggers.maxtransdepth == 0);
-	Assert(afterTriggerFiringDepth == 0);
 }
 
 
@@ -5194,7 +5196,7 @@ AfterTriggerEndQuery(EState *estate)
 	 */
 	qs = &afterTriggers.query_stack[afterTriggers.query_depth];
 
-	afterTriggerFiringDepth++;
+	afterTriggers.firing_depth++;
 	for (;;)
 	{
 		if (afterTriggerMarkEvents(&qs->events, &afterTriggers.events, true))
@@ -5245,7 +5247,7 @@ AfterTriggerEndQuery(EState *estate)
 	AfterTriggerFreeQuery(&afterTriggers.query_stack[afterTriggers.query_depth]);
 
 	afterTriggers.query_depth--;
-	afterTriggerFiringDepth--;
+	afterTriggers.firing_depth--;
 }
 
 
@@ -5341,7 +5343,7 @@ AfterTriggerFireDeferred(void)
 	 * Run all the remaining triggers.  Loop until they are all gone, in case
 	 * some trigger queues more for us to do.
 	 */
-	afterTriggerFiringDepth++;
+	afterTriggers.firing_depth++;
 	while (afterTriggerMarkEvents(events, NULL, false))
 	{
 		CommandId	firing_id = afterTriggers.firing_counter++;
@@ -5353,7 +5355,7 @@ AfterTriggerFireDeferred(void)
 	/* Flush any fast-path batches accumulated by the triggers just fired. */
 	FireAfterTriggerBatchCallbacks();
 
-	afterTriggerFiringDepth--;
+	afterTriggers.firing_depth--;
 
 	/*
 	 * We don't bother freeing the event list, since it will go away anyway
@@ -5420,7 +5422,7 @@ AfterTriggerEndXact(bool isCommit)
 	/* No more afterTriggers manipulation until next transaction starts. */
 	afterTriggers.query_depth = -1;
 
-	afterTriggerFiringDepth = 0;
+	afterTriggers.firing_depth = 0;
 
 	Assert(afterTriggers.batch_callbacks == NIL || !isCommit);
 
@@ -6079,7 +6081,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
 		AfterTriggerEventList *events = &afterTriggers.events;
 		bool		snapshot_set = false;
 
-		afterTriggerFiringDepth++;
+		afterTriggers.firing_depth++;
 		while (afterTriggerMarkEvents(events, NULL, true))
 		{
 			CommandId	firing_id = afterTriggers.firing_counter++;
@@ -6113,7 +6115,7 @@ AfterTriggerSetState(ConstraintsSetStmt *stmt)
 		 * Flush any fast-path batches accumulated by the triggers just fired.
 		 */
 		FireAfterTriggerBatchCallbacks();
-		afterTriggerFiringDepth--;
+		afterTriggers.firing_depth--;
 
 		if (snapshot_set)
 			PopActiveSnapshot();
@@ -6837,11 +6839,11 @@ RegisterAfterTriggerBatchCallback(AfterTriggerBatchCallback callback,
 	 * Must be called while afterTriggers is active; callbacks registered
 	 * outside a trigger-firing context would never fire.
 	 */
-	Assert(afterTriggerFiringDepth > 0);
+	Assert(afterTriggers.firing_depth > 0);
 	oldcxt = MemoryContextSwitchTo(TopTransactionContext);
 	item = palloc(sizeof(AfterTriggerCallbackItem));
 	item->callback = callback;
-	item->firing_depth = afterTriggerFiringDepth;
+	item->firing_depth = afterTriggers.firing_depth;
 	item->arg = arg;
 	afterTriggers.batch_callbacks =
 		lappend(afterTriggers.batch_callbacks, item);
@@ -6876,7 +6878,7 @@ FireAfterTriggerBatchCallbacks(void)
 	{
 		AfterTriggerCallbackItem *item = lfirst(lc);
 
-		if (item->firing_depth == afterTriggerFiringDepth)
+		if (item->firing_depth == afterTriggers.firing_depth)
 			to_fire = lappend(to_fire, item);
 		else
 			remaining = lappend(remaining, item);
@@ -6908,5 +6910,5 @@ FireAfterTriggerBatchCallbacks(void)
 bool
 AfterTriggerIsActive(void)
 {
-	return afterTriggerFiringDepth > 0;
+	return afterTriggers.firing_depth > 0;
 }
-- 
2.47.3

