From e4186762ffaa4188e16702e8f4f299ea70988b96 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 29 Aug 2002 17:14:33 +0000
Subject: [PATCH] Adjust nodeFunctionscan.c to reset transient memory context
 between calls to the table function, thus preventing memory leakage
 accumulation across calls.  This means that SRFs need to be careful to
 distinguish permanent and local storage; adjust code and documentation
 accordingly.  Patch by Joe Conway, very minor tweaks by Tom Lane.

---
 contrib/pgstattuple/README.pgstattuple  | 10 ++--
 contrib/pgstattuple/pgstattuple.c       | 78 ++++++++-----------------
 contrib/pgstattuple/pgstattuple.sql.in  | 26 ++++-----
 contrib/tablefunc/tablefunc.c           | 28 ++++++---
 doc/src/sgml/xfunc.sgml                 | 46 +++++++++++----
 src/backend/executor/nodeFunctionscan.c |  8 ++-
 src/backend/utils/adt/lockfuncs.c       | 41 +++++++------
 src/backend/utils/fmgr/funcapi.c        | 21 +++----
 src/backend/utils/misc/guc.c            |  8 ++-
 src/include/funcapi.h                   | 22 ++++---
 10 files changed, 154 insertions(+), 134 deletions(-)

diff --git a/contrib/pgstattuple/README.pgstattuple b/contrib/pgstattuple/README.pgstattuple
index 804c37c6a91..42a7fc2165d 100644
--- a/contrib/pgstattuple/README.pgstattuple
+++ b/contrib/pgstattuple/README.pgstattuple
@@ -1,4 +1,4 @@
-pgstattuple README			2002/08/22 Tatsuo Ishii
+pgstattuple README			2002/08/29 Tatsuo Ishii
 
 1. What is pgstattuple?
 
@@ -40,15 +40,15 @@ free_percent		-- free space in %
 
 3. Using pgstattuple
 
-   pgstattuple may be called as a SRF (set returning function) and is
+   pgstattuple may be called as a table function and is
    defined as follows:
 
-   CREATE OR REPLACE FUNCTION pgstattuple(text) RETURNS SETOF pgstattuple_view
+   CREATE OR REPLACE FUNCTION pgstattuple(text) RETURNS pgstattuple_type
      AS 'MODULE_PATHNAME', 'pgstattuple'
      LANGUAGE 'c' WITH (isstrict);
 
-   The argument is the table name.  Note that pgstattuple never
-   returns more than 1 tuple.
+   The argument is the table name.  Note that pgstattuple only returns
+   one row.
 
 4. Notes
 
diff --git a/contrib/pgstattuple/pgstattuple.c b/contrib/pgstattuple/pgstattuple.c
index a357f6f412a..4fbc60bcf2a 100644
--- a/contrib/pgstattuple/pgstattuple.c
+++ b/contrib/pgstattuple/pgstattuple.c
@@ -1,5 +1,5 @@
 /*
- * $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.7 2002/08/23 08:19:49 ishii Exp $
+ * $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.8 2002/08/29 17:14:31 tgl Exp $
  *
  * Copyright (c) 2001,2002  Tatsuo Ishii
  *
@@ -25,10 +25,10 @@
 #include "postgres.h"
 
 #include "fmgr.h"
+#include "funcapi.h"
 #include "access/heapam.h"
 #include "access/transam.h"
 #include "catalog/namespace.h"
-#include "funcapi.h"
 #include "utils/builtins.h"
 
 
@@ -41,19 +41,19 @@ extern Datum pgstattuple(PG_FUNCTION_ARGS);
  * returns live/dead tuples info
  *
  * C FUNCTION definition
- * pgstattuple(TEXT) returns setof pgstattuple_view
- * see pgstattuple.sql for pgstattuple_view
+ * pgstattuple(text) returns pgstattuple_type
+ * see pgstattuple.sql for pgstattuple_type
  * ----------
  */
 
-#define DUMMY_TUPLE "pgstattuple_view"
+#define DUMMY_TUPLE "pgstattuple_type"
 #define NCOLUMNS 9
 #define NCHARS 32
 
 Datum
 pgstattuple(PG_FUNCTION_ARGS)
 {
-	text	   *relname;
+	text	   *relname = PG_GETARG_TEXT_P(0);
 	RangeVar   *relrv;
 	Relation	rel;
 	HeapScanDesc scan;
@@ -71,62 +71,30 @@ pgstattuple(PG_FUNCTION_ARGS)
 	double		dead_tuple_percent;
 	uint64		free_space = 0; /* free/reusable space in bytes */
 	double		free_percent;	/* free/reusable space in % */
-
-	FuncCallContext	   *funcctx;
-	int					call_cntr;
-	int					max_calls;
 	TupleDesc			tupdesc;
 	TupleTableSlot	   *slot;
 	AttInMetadata	   *attinmeta;
+	char			  **values;
+	int					i;
+	Datum				result;
 
-	char **values;
-	int i;
-	Datum		result;
-
-	/* stuff done only on the first call of the function */
-	if(SRF_IS_FIRSTCALL())
-	{
-		/* create a function context for cross-call persistence */
-		funcctx = SRF_FIRSTCALL_INIT();
-    
-		/* total number of tuples to be returned */
-		funcctx->max_calls = 1;
-    
-		/*
-		 * Build a tuple description for a pgstattupe_view tuple
-		 */
-		tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE);
-    
-		/* allocate a slot for a tuple with this tupdesc */
-		slot = TupleDescGetSlot(tupdesc);
-    
-		/* assign slot to function context */
-		funcctx->slot = slot;
-    
-		/*
-		 * Generate attribute metadata needed later to produce tuples from raw
-		 * C strings
-		 */
-		attinmeta = TupleDescGetAttInMetadata(tupdesc);
-		funcctx->attinmeta = attinmeta;
-	}
+	/*
+	 * Build a tuple description for a pgstattupe_type tuple
+	 */
+	tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE);
 
-	/* stuff done on every call of the function */
-	funcctx = SRF_PERCALL_SETUP();
-	call_cntr = funcctx->call_cntr;
-	max_calls = funcctx->max_calls;
-	slot = funcctx->slot;
-	attinmeta = funcctx->attinmeta;
+	/* allocate a slot for a tuple with this tupdesc */
+	slot = TupleDescGetSlot(tupdesc);
 
-	/* Are we done? */
-	if (call_cntr >= max_calls)
-	{
-		SRF_RETURN_DONE(funcctx);
-	}
+	/*
+	 * Generate attribute metadata needed later to produce tuples from raw
+	 * C strings
+	 */
+	attinmeta = TupleDescGetAttInMetadata(tupdesc);
 
 	/* open relation */
-	relname = PG_GETARG_TEXT_P(0);
-	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,"pgstattuple"));
+	relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
+															 "pgstattuple"));
 	rel = heap_openrv(relrv, AccessShareLock);
 
 	nblocks = RelationGetNumberOfBlocks(rel);
@@ -223,5 +191,5 @@ pgstattuple(PG_FUNCTION_ARGS)
 	}
 	pfree(values);
     
-	SRF_RETURN_NEXT(funcctx, result);
+	PG_RETURN_DATUM(result);
 }
diff --git a/contrib/pgstattuple/pgstattuple.sql.in b/contrib/pgstattuple/pgstattuple.sql.in
index 7c661a8ee3c..d3370fb830c 100644
--- a/contrib/pgstattuple/pgstattuple.sql.in
+++ b/contrib/pgstattuple/pgstattuple.sql.in
@@ -1,16 +1,16 @@
-DROP VIEW pgstattuple_view CASCADE;
-CREATE VIEW pgstattuple_view AS
-  SELECT
-    0::BIGINT AS table_len,		-- physical table length in bytes
-    0::BIGINT AS tuple_count,		-- number of live tuples
-    0::BIGINT AS tuple_len,		-- total tuples length in bytes
-    0.0::FLOAT AS tuple_percent,	-- live tuples in %
-    0::BIGINT AS dead_tuple_count,	-- number of dead tuples
-    0::BIGINT AS dead_tuple_len,	-- total dead tuples length in bytes
-    0.0::FLOAT AS dead_tuple_percent,	-- dead tuples in %
-    0::BIGINT AS free_space,		-- free space in bytes
-    0.0::FLOAT AS free_percent;		-- free space in %
+DROP TYPE pgstattuple_type CASCADE;
+CREATE TYPE pgstattuple_type AS (
+    table_len BIGINT,		-- physical table length in bytes
+    tuple_count BIGINT,		-- number of live tuples
+    tuple_len BIGINT,		-- total tuples length in bytes
+    tuple_percent FLOAT,	-- live tuples in %
+    dead_tuple_count BIGINT,	-- number of dead tuples
+    dead_tuple_len BIGINT,	-- total dead tuples length in bytes
+    dead_tuple_percent FLOAT,	-- dead tuples in %
+    free_space BIGINT,		-- free space in bytes
+    free_percent FLOAT		-- free space in %
+);
 
-CREATE OR REPLACE FUNCTION pgstattuple(text) RETURNS SETOF pgstattuple_view
+CREATE OR REPLACE FUNCTION pgstattuple(text) RETURNS pgstattuple_type
   AS 'MODULE_PATHNAME', 'pgstattuple'
   LANGUAGE 'c' WITH (isstrict);
diff --git a/contrib/tablefunc/tablefunc.c b/contrib/tablefunc/tablefunc.c
index d05fc1a76b6..37a6e723a65 100644
--- a/contrib/tablefunc/tablefunc.c
+++ b/contrib/tablefunc/tablefunc.c
@@ -87,6 +87,7 @@ normal_rand(PG_FUNCTION_ARGS)
 	float8				stddev;
 	float8				carry_val;
 	bool				use_carry;
+	MemoryContext		oldcontext;
 
 	/* stuff done only on the first call of the function */
  	if(SRF_IS_FIRSTCALL())
@@ -94,6 +95,9 @@ normal_rand(PG_FUNCTION_ARGS)
 		/* create a function context for cross-call persistence */
  		funcctx = SRF_FIRSTCALL_INIT();
 
+		/* switch to memory context appropriate for multiple function calls */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
 		/* total number of tuples to be returned */
 		funcctx->max_calls = PG_GETARG_UINT32(0);
 
@@ -119,6 +123,8 @@ normal_rand(PG_FUNCTION_ARGS)
 		 * purpose it doesn't matter, just cast it as an unsigned value
 		 */
 		srandom(PG_GETARG_UINT32(3));
+
+		MemoryContextSwitchTo(oldcontext);
     }
 
 	/* stuff done on every call of the function */
@@ -260,10 +266,11 @@ crosstab(PG_FUNCTION_ARGS)
 	AttInMetadata	   *attinmeta;
 	SPITupleTable	   *spi_tuptable = NULL;
 	TupleDesc			spi_tupdesc;
-	char			   *lastrowid;
+	char			   *lastrowid = NULL;
 	crosstab_fctx	   *fctx;
 	int					i;
 	int					num_categories;
+	MemoryContext		oldcontext;
 
 	/* stuff done only on the first call of the function */
  	if(SRF_IS_FIRSTCALL())
@@ -275,13 +282,12 @@ crosstab(PG_FUNCTION_ARGS)
 		TupleDesc		tupdesc = NULL;
 		int				ret;
 		int				proc;
-		MemoryContext	oldcontext;
 
 		/* create a function context for cross-call persistence */
  		funcctx = SRF_FIRSTCALL_INIT();
 
-		/* SPI switches context on us, so save it first */
-		oldcontext = CurrentMemoryContext;
+		/* switch to memory context appropriate for multiple function calls */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
 		/* Connect to SPI manager */
 		if ((ret = SPI_connect()) < 0)
@@ -317,8 +323,8 @@ crosstab(PG_FUNCTION_ARGS)
 	 		SRF_RETURN_DONE(funcctx);
 		}
 
-		/* back to the original memory context */
-		MemoryContextSwitchTo(oldcontext);
+		/* SPI switches context on us, so reset it */
+		MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
 		/* get the typeid that represents our return type */
 		functypeid = get_func_rettype(funcid);
@@ -381,6 +387,8 @@ crosstab(PG_FUNCTION_ARGS)
 
 		/* total number of tuples to be returned */
 		funcctx->max_calls = proc;
+
+		MemoryContextSwitchTo(oldcontext);
     }
 
 	/* stuff done on every call of the function */
@@ -432,7 +440,7 @@ crosstab(PG_FUNCTION_ARGS)
 			for (i = 0; i < num_categories; i++)
 			{
 				HeapTuple	spi_tuple;
-				char	   *rowid;
+				char	   *rowid = NULL;
 
 				/* see if we've gone too far already */
 				if (call_cntr >= max_calls)
@@ -496,7 +504,13 @@ crosstab(PG_FUNCTION_ARGS)
 			xpfree(fctx->lastrowid);
 
 			if (values[0] != NULL)
+			{
+				/* switch to memory context appropriate for multiple function calls */
+				oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
 				lastrowid = fctx->lastrowid = pstrdup(values[0]);
+				MemoryContextSwitchTo(oldcontext);
+			}
 
 			if (!allnulls)
 			{
diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml
index b3f653a28a1..fad7ad888d8 100644
--- a/doc/src/sgml/xfunc.sgml
+++ b/doc/src/sgml/xfunc.sgml
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.57 2002/08/29 00:17:02 tgl Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.58 2002/08/29 17:14:32 tgl Exp $
 -->
 
  <chapter id="xfunc">
@@ -1670,13 +1670,14 @@ typedef struct
 	AttInMetadata	   *attinmeta;
 
 	/*
-	 * memory context used to initialize structure
+	 * memory context used for structures which must live for multiple calls
 	 *
-	 * fmctx is set by SRF_FIRSTCALL_INIT() for you, and used by
-	 * SRF_RETURN_DONE() for cleanup. It is primarily for internal use
-	 * by the API.
+	 * multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used
+	 * by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory
+	 * context for any memory that is to be re-used across multiple calls
+	 * of the SRF.
 	 */
-	MemoryContext	fmctx;
+	MemoryContext	multi_call_memory_ctx;
 
 }	FuncCallContext;
 </programlisting>
@@ -1714,27 +1715,43 @@ SRF_RETURN_DONE(funcctx)
      to clean up and end the SRF.
     </para>
 
+    <para>
+     The palloc memory context that is current when the SRF is called is
+     a transient context that will be cleared between calls.  This means
+     that you do not need to be careful about pfree'ing everything
+     you palloc; it will go away anyway.  However, if you want to allocate
+     any data structures to live across calls, you need to put them somewhere
+     else.  The memory context referenced by
+     <structfield>multi_call_memory_ctx</> is a suitable location for any
+     data that needs to survive until the SRF is finished running.  In most
+     cases, this means that you should switch into
+     <structfield>multi_call_memory_ctx</> while doing the first-call setup.
+    </para>
+
     <para>
      A complete pseudo-code example looks like the following:
 <programlisting>
 Datum
 my_Set_Returning_Function(PG_FUNCTION_ARGS)
 {
-    FuncCallContext     *funcctx;
-    Datum                result;
+    FuncCallContext  *funcctx;
+    Datum             result;
+    MemoryContext     oldcontext;
     [user defined declarations]
 
     if (SRF_IS_FIRSTCALL())
     {
+        funcctx = SRF_FIRSTCALL_INIT();
+        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
         /* one-time setup code appears here: */
         [user defined code]
-        funcctx = SRF_FIRSTCALL_INIT();
         [if returning composite]
             [build TupleDesc, and perhaps AttInMetadata]
             [obtain slot]
             funcctx-&gt;slot = slot;
         [endif returning composite]
         [user defined code]
+        MemoryContextSwitchTo(oldcontext);
     }
 
     /* each-time setup code appears here: */
@@ -1777,8 +1794,13 @@ testpassbyval(PG_FUNCTION_ARGS)
      /* stuff done only on the first call of the function */
      if (SRF_IS_FIRSTCALL())
      {
+        MemoryContext	oldcontext;
+
         /* create a function context for cross-call persistence */
-         funcctx = SRF_FIRSTCALL_INIT();
+        funcctx = SRF_FIRSTCALL_INIT();
+
+        /* switch to memory context appropriate for multiple function calls */
+        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
 
         /* total number of tuples to be returned */
         funcctx-&gt;max_calls = PG_GETARG_UINT32(0);
@@ -1800,6 +1822,8 @@ testpassbyval(PG_FUNCTION_ARGS)
          */
         attinmeta = TupleDescGetAttInMetadata(tupdesc);
         funcctx-&gt;attinmeta = attinmeta;
+
+        MemoryContextSwitchTo(oldcontext);
     }
 
     /* stuff done on every call of the function */
@@ -1836,7 +1860,7 @@ testpassbyval(PG_FUNCTION_ARGS)
         /* make the tuple into a datum */
         result = TupleGetDatum(slot, tuple);
 
-        /* Clean up */
+        /* Clean up (this is not actually necessary) */
         pfree(values[0]);
         pfree(values[1]);
         pfree(values[2]);
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 381b6047bf0..d58d312238e 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.6 2002/08/29 00:17:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.7 2002/08/29 17:14:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -428,6 +428,12 @@ function_getonetuple(FunctionScanState *scanstate,
 	ExprContext	   *econtext = scanstate->csstate.cstate.cs_ExprContext;
 	TupleTableSlot *slot = scanstate->csstate.css_ScanTupleSlot;
 
+	/*
+	 * reset per-tuple memory context before each call of the function.
+	 * This cleans up any local memory the function may leak when called.
+	 */
+	ResetExprContext(econtext);
+
 	/*
 	 * get the next Datum from the function
 	 */
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 83d0d1051df..199efbacd26 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -5,7 +5,7 @@
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *		$Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.3 2002/08/29 00:17:05 tgl Exp $
+ *		$Header: /cvsroot/pgsql/src/backend/utils/adt/lockfuncs.c,v 1.4 2002/08/29 17:14:33 tgl Exp $
  */
 #include "postgres.h"
 
@@ -24,15 +24,20 @@ static int next_lock(int locks[]);
 Datum
 pg_lock_status(PG_FUNCTION_ARGS)
 {
-	FuncCallContext		*funccxt;
-	LockData			*lockData;
+	FuncCallContext	   *funcctx;
+	LockData		   *lockData;
+	MemoryContext		oldcontext;
 
 	if (SRF_IS_FIRSTCALL())
 	{
-		MemoryContext	oldcxt;
 		TupleDesc		tupdesc;
 
-		funccxt = SRF_FIRSTCALL_INIT();
+		/* create a function context for cross-call persistence */
+		funcctx = SRF_FIRSTCALL_INIT();
+
+		/* switch to memory context appropriate for multiple function calls */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
 		tupdesc = CreateTemplateTupleDesc(5, WITHOUTOID);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "relation",
 						   OIDOID, -1, 0, false);
@@ -45,10 +50,8 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		TupleDescInitEntry(tupdesc, (AttrNumber) 5, "isgranted",
 						   BOOLOID, -1, 0, false);
 
-		funccxt->slot = TupleDescGetSlot(tupdesc);
-		funccxt->attinmeta = TupleDescGetAttInMetadata(tupdesc);
-
-		oldcxt = MemoryContextSwitchTo(funccxt->fmctx);
+		funcctx->slot = TupleDescGetSlot(tupdesc);
+		funcctx->attinmeta = TupleDescGetAttInMetadata(tupdesc);
 
 		/*
 		 * Preload all the locking information that we will eventually format
@@ -56,15 +59,15 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		 * MemoryContext is reset when the SRF finishes, we don't need to
 		 * free it ourselves.
 		 */
-		funccxt->user_fctx = (LockData *) palloc(sizeof(LockData));
+		funcctx->user_fctx = (LockData *) palloc(sizeof(LockData));
 
-		GetLockStatusData(funccxt->user_fctx);
+		GetLockStatusData(funcctx->user_fctx);
 
-		MemoryContextSwitchTo(oldcxt);
+		MemoryContextSwitchTo(oldcontext);
 	}
 
-	funccxt	= SRF_PERCALL_SETUP();
-	lockData = (LockData *) funccxt->user_fctx;
+	funcctx	= SRF_PERCALL_SETUP();
+	lockData = (LockData *) funcctx->user_fctx;
 
 	while (lockData->currIdx < lockData->nelements)
 	{
@@ -82,7 +85,7 @@ pg_lock_status(PG_FUNCTION_ARGS)
 		holder		= &(lockData->holders[currIdx]);
 		lock		= &(lockData->locks[currIdx]);
 		proc		= &(lockData->procs[currIdx]);
-		num_attrs	= funccxt->attinmeta->tupdesc->natts;
+		num_attrs	= funcctx->attinmeta->tupdesc->natts;
 
 		values = (char **) palloc(sizeof(*values) * num_attrs);
 
@@ -133,12 +136,12 @@ pg_lock_status(PG_FUNCTION_ARGS)
 
 		strncpy(values[3], GetLockmodeName(mode), 32);
 
-		tuple = BuildTupleFromCStrings(funccxt->attinmeta, values);
-		result = TupleGetDatum(funccxt->slot, tuple);
-		SRF_RETURN_NEXT(funccxt, result);
+		tuple = BuildTupleFromCStrings(funcctx->attinmeta, values);
+		result = TupleGetDatum(funcctx->slot, tuple);
+		SRF_RETURN_NEXT(funcctx, result);
 	}
 
-	SRF_RETURN_DONE(funccxt);
+	SRF_RETURN_DONE(funcctx);
 }
 
 static LOCKMODE
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 28311c26b7b..35ba972fe12 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -7,7 +7,7 @@
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/funcapi.c,v 1.3 2002/08/29 00:17:05 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/fmgr/funcapi.c,v 1.4 2002/08/29 17:14:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -39,16 +39,12 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
 	{
 		/*
 		 * First call
+		 *
+		 * Allocate suitably long-lived space and zero it
 		 */
-		MemoryContext oldcontext;
-
-		/* switch to the appropriate memory context */
-		oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
-
-		/*
-		 * allocate space and zero it
-		 */
-		retval = (FuncCallContext *) palloc(sizeof(FuncCallContext));
+		retval = (FuncCallContext *)
+			MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+							   sizeof(FuncCallContext));
 		MemSet(retval, 0, sizeof(FuncCallContext));
 
 		/*
@@ -59,15 +55,12 @@ init_MultiFuncCall(PG_FUNCTION_ARGS)
 		retval->slot = NULL;
 		retval->user_fctx = NULL;
 		retval->attinmeta = NULL;
-		retval->fmctx = fcinfo->flinfo->fn_mcxt;
+		retval->multi_call_memory_ctx = fcinfo->flinfo->fn_mcxt;
 
 		/*
 		 * save the pointer for cross-call use
 		 */
 		fcinfo->flinfo->fn_extra = retval;
-
-		/* back to the original memory context */
-		MemoryContextSwitchTo(oldcontext);
 	}
 	else	/* second and subsequent calls */
 	{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 660cd124ba9..5114fcc38f2 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -5,7 +5,7 @@
  * command, configuration file, and command line options.
  * See src/backend/utils/misc/README for more information.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.85 2002/08/29 00:17:05 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/misc/guc.c,v 1.86 2002/08/29 17:14:33 tgl Exp $
  *
  * Copyright 2000 by PostgreSQL Global Development Group
  * Written by Peter Eisentraut <peter_e@gmx.net>.
@@ -2421,6 +2421,7 @@ show_all_settings(PG_FUNCTION_ARGS)
 	int					max_calls;
 	TupleTableSlot	   *slot;
 	AttInMetadata	   *attinmeta;
+	MemoryContext		oldcontext;
 
 	/* stuff done only on the first call of the function */
  	if(SRF_IS_FIRSTCALL())
@@ -2428,6 +2429,9 @@ show_all_settings(PG_FUNCTION_ARGS)
 		/* create a function context for cross-call persistence */
  		funcctx = SRF_FIRSTCALL_INIT();
 
+		/* switch to memory context appropriate for multiple function calls */
+		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+
 		/* need a tuple descriptor representing two TEXT columns */
 		tupdesc = CreateTemplateTupleDesc(2, WITHOUTOID);
 		TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
@@ -2450,6 +2454,8 @@ show_all_settings(PG_FUNCTION_ARGS)
 
 		/* total number of tuples to be returned */
 		funcctx->max_calls = GetNumConfigOptions();
+
+		MemoryContextSwitchTo(oldcontext);
     }
 
 	/* stuff done on every call of the function */
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index 27dbdf20e62..fcfb6acb694 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -9,7 +9,7 @@
  *
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
- * $Id: funcapi.h,v 1.5 2002/08/29 00:17:06 tgl Exp $
+ * $Id: funcapi.h,v 1.6 2002/08/29 17:14:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,13 +101,14 @@ typedef struct FuncCallContext
 	AttInMetadata	   *attinmeta;
 
 	/*
-	 * memory context used to initialize structure
+	 * memory context used for structures which must live for multiple calls
 	 *
-	 * fmctx is set by SRF_FIRSTCALL_INIT() for you, and used by
-	 * SRF_RETURN_DONE() for cleanup. It is primarily for internal use
-	 * by the API.
+	 * multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used
+	 * by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory
+	 * context for any memory that is to be re-used across multiple calls
+	 * of the SRF.
 	 */
-	MemoryContext	fmctx;
+	MemoryContext	multi_call_memory_ctx;
 
 }	FuncCallContext;
 
@@ -160,17 +161,22 @@ extern HeapTuple BuildTupleFromCStrings(AttInMetadata *attinmeta, char **values)
  * {
  * 	FuncCallContext	   *funcctx;
  * 	Datum				result;
+ *  MemoryContext		oldcontext;
  * 	<user defined declarations>
  * 
- * 	if(SRF_IS_FIRSTCALL())
+ * 	if (SRF_IS_FIRSTCALL())
  * 	{
- * 		<user defined code>
  * 		funcctx = SRF_FIRSTCALL_INIT();
+ *		// switch context when allocating stuff to be used in later calls
+ *		oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ * 		<user defined code>
  * 		<if returning composite>
  * 			<obtain slot>
  * 			funcctx->slot = slot;
  * 		<endif returning composite>
  * 		<user defined code>
+ *		// return to original context when allocating transient memory
+ *		MemoryContextSwitchTo(oldcontext);
  *  }
  * 	<user defined code>
  * 	funcctx = SRF_PERCALL_SETUP();
-- 
GitLab