From ff428cdeda20b8b1cf465f8c6cd27da9b2abe9b7 Mon Sep 17 00:00:00 2001
From: Neil Conway <neilc@samurai.com>
Date: Fri, 29 Feb 2008 02:49:39 +0000
Subject: [PATCH] Fix several memory leaks when rescanning SRFs. Arrange for an
 SRF's "multi_call_ctx" to be a distinct sub-context of the EState's per-query
 context, and delete the multi_call_ctx as soon as the SRF finishes execution.
 This avoids leaking SRF memory until the end of the current query, which is
 particularly egregious when the SRF is scanned multiple times. This change
 also fixes a leak of the fields of the AttInMetadata struct in
 shutdown_MultiFuncCall().

Also fix a leak of the SRF result TupleDesc when rescanning a
FunctionScan node. The TupleDesc is allocated in the per-query context
for every call to ExecMakeTableFunctionResult(), so we should free it
after calling that function. Since the SRF might choose to return
a non-expendable TupleDesc, we only free the TupleDesc if it is
not being reference-counted.

Backpatch to 8.3 and 8.2 stable branches.
---
 src/backend/executor/nodeFunctionscan.c | 12 +++++++++-
 src/backend/utils/fmgr/funcapi.c        | 29 ++++++++++++++++---------
 2 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 81b9c69e9a2..6bbb5b139b6 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.45 2008/01/01 19:45:49 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/executor/nodeFunctionscan.c,v 1.46 2008/02/29 02:49:39 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -77,7 +77,17 @@ FunctionNext(FunctionScanState *node)
 		 * do it always.
 		 */
 		if (funcTupdesc)
+		{
 			tupledesc_match(node->tupdesc, funcTupdesc);
+
+			/*
+			 * If it is a dynamically-allocated TupleDesc, free it: it is
+			 * typically allocated in the EState's per-query context, so we
+			 * must avoid leaking it on rescan.
+			 */
+			if (funcTupdesc->tdrefcount == -1)
+				FreeTupleDesc(funcTupdesc);
+		}
 	}
 
 	/*
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 8c6609de62c..4bb230a3f36 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -7,7 +7,7 @@
  * Copyright (c) 2002-2008, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.37 2008/01/01 19:45:53 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.38 2008/02/29 02:49:39 neilc Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
+#include "utils/memutils.h"
 #include "utils/syscache.h"
 #include "utils/typcache.h"
 
@@ -63,13 +64,23 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
 		/*
 		 * First call
 		 */
-		ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+		ReturnSetInfo  *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
+		MemoryContext	multi_call_ctx;
+
+		/*
+		 * Create a suitably long-lived context to hold cross-call data
+		 */
+		multi_call_ctx = AllocSetContextCreate(fcinfo->flinfo->fn_mcxt,
+											   "SRF multi-call context",
+											   ALLOCSET_SMALL_MINSIZE,
+											   ALLOCSET_SMALL_INITSIZE,
+											   ALLOCSET_SMALL_MAXSIZE);
 
 		/*
 		 * Allocate suitably long-lived space and zero it
 		 */
 		retval = (FuncCallContext *)
-			MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
+			MemoryContextAllocZero(multi_call_ctx,
 								   sizeof(FuncCallContext));
 
 		/*
@@ -81,7 +92,7 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
 		retval->user_fctx = NULL;
 		retval->attinmeta = NULL;
 		retval->tuple_desc = NULL;
-		retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt;
+		retval->multi_call_memory_ctx = multi_call_ctx;
 
 		/*
 		 * save the pointer for cross-call use
@@ -168,13 +179,11 @@ shutdown_MultiFuncCall(Datum arg)
 	flinfo->fn_extra = NULL;
 
 	/*
-	 * Caller is responsible to free up memory for individual struct elements
-	 * other than att_in_funcinfo and elements.
+	 * Delete context that holds all multi-call data, including the
+	 * FuncCallContext itself
 	 */
-	if (funcctx->attinmeta != NULL)
-		pfree(funcctx->attinmeta);
-
-	pfree(funcctx);
+	MemoryContextSwitchTo(flinfo->fn_mcxt);
+	MemoryContextDelete(funcctx->multi_call_memory_ctx);
 }
 
 
-- 
GitLab