Adjust nodeFunctionscan.c to reset transient memory context between calls
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 29 Aug 2002 17:14:33 +0000 (17:14 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 29 Aug 2002 17:14:33 +0000 (17:14 +0000)
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
contrib/pgstattuple/pgstattuple.c
contrib/pgstattuple/pgstattuple.sql.in
contrib/tablefunc/tablefunc.c
doc/src/sgml/xfunc.sgml
src/backend/executor/nodeFunctionscan.c
src/backend/utils/adt/lockfuncs.c
src/backend/utils/fmgr/funcapi.c
src/backend/utils/misc/guc.c
src/include/funcapi.h

index 804c37c6a91a367bc658688027aa15e430f8c669..42a7fc2165dddcb6c81d9c9909d78639cf900333 100644 (file)
@@ -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
 
index a357f6f412ad26f598a52b88232fb87b14471a7b..4fbc60bcf2aa4a0bc1a03e89ac63b56150a9efc6 100644 (file)
@@ -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
  *
 #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);
 }
index 7c661a8ee3c21d5b3bfef624a137d477f9f21e46..d3370fb830cda48588ef3ef7d97004e92a91bb2c 100644 (file)
@@ -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);
index d05fc1a76b66c04444218e3fb8689b6b9ccb038c..37a6e723a65862228717d1b62e3acfde5664d665 100644 (file)
@@ -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)
                        {
index b3f653a28a12fc59a1770e23875188a95068c514..fad7ad888d8931c2e440cf3cb2ac7bfc565783e1 100644 (file)
@@ -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]);
index 381b6047bf0729b022cd583d385544b50abac2df..d58d312238e66d4fd8c99a45cf9d5630ebfd54e8 100644 (file)
@@ -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
         */
index 83d0d1051df974056d7122a049f1c79762f0ebb8..199efbacd26bdfdd20d88480f287db8eb5b51396 100644 (file)
@@ -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
index 28311c26b7b2ec3db8901244afb10d4d8beb7bc0..35ba972fe12051d536fc2dc1a384c06ff2a5300c 100644 (file)
@@ -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 */
        {
index 660cd124ba90c6afec74c5bb34a5d760109af516..5114fcc38f29d5745e0a7067b169eb1f7fac1127 100644 (file)
@@ -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 */
index 27dbdf20e62061a9207d0bee6ca64ad464807733..fcfb6acb69448a7f9a25698447ee8326ede4fdba 100644 (file)
@@ -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();