From 8f55b9a8ba6d6543c162927c29f8d06ce1cd7372 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 1 Jul 2007 17:45:42 +0000
Subject: [PATCH] Avoid memory leakage when a series of subtransactions invoke
 AFTER triggers that are fired at end-of-statement (as is the normal case for
 foreign keys, for example).  In this situation the per-subxact deferred
 trigger context is always empty when subtransaction exit is reached; so we
 could free it, but were not doing so, leading to an intratransaction leak of
 8K or more per subtransaction.  Per off-list example from Viatcheslav Kalinin
 subsequent to bug #3418 (his original bug report omitted a foreign key
 constraint needed to cause this leak).

Back-patch to 8.2; prior versions were not using per-subxact contexts
for deferred triggers, so did not have this leak.
---
 src/backend/commands/trigger.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index e2dadb7a7e2..3f251ad6dd5 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.214 2007/03/19 23:38:29 wieck Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/trigger.c,v 1.215 2007/07/01 17:45:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2814,6 +2814,24 @@ AfterTriggerEndSubXact(bool isCommit)
 		afterTriggers->state_stack[my_level] = NULL;
 		Assert(afterTriggers->query_depth ==
 			   afterTriggers->depth_stack[my_level]);
+		/*
+		 * It's entirely possible that the subxact created an event_cxt but
+		 * there is not anything left in it (because all the triggers were
+		 * fired at end-of-statement).  If so, we should release the context
+		 * to prevent memory leakage in a long sequence of subtransactions.
+		 * We can detect whether there's anything of use in the context by
+		 * seeing if anything was added to the global events list since
+		 * subxact start.  (This test doesn't catch every case where the
+		 * context is deletable; for instance maybe the only additions were
+		 * from a sub-sub-xact.  But it handles the common case.)
+		 */
+		if (afterTriggers->cxt_stack[my_level] &&
+			afterTriggers->events.tail == afterTriggers->events_stack[my_level].tail)
+		{
+			MemoryContextDelete(afterTriggers->cxt_stack[my_level]);
+			/* avoid double delete if abort later */
+			afterTriggers->cxt_stack[my_level] = NULL;
+		}
 	}
 	else
 	{
-- 
GitLab