Fix several memory leaks when rescanning SRFs. Arrange for an SRF's
authorNeil Conway <neilc@samurai.com>
Fri, 29 Feb 2008 02:49:43 +0000 (02:49 +0000)
committerNeil Conway <neilc@samurai.com>
Fri, 29 Feb 2008 02:49:43 +0000 (02:49 +0000)
"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
src/backend/utils/fmgr/funcapi.c

index f3a5ad1956e82dc91bd69d3d9b504f8e07acba49..c644c92edfb675ca54499bccd4a6a66cf43c65da 100644 (file)
@@ -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);
+               }
        }
 
        /*
index 143d39a549ebfbc2afc8c419dbce345f4f933f17..23db8f4f711a06a2723994b10e542e99120238f5 100644 (file)
@@ -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);
 }