Adjust nodeFunctionscan.c to reset transient memory context between calls
authorTom Lane
Thu, 29 Aug 2002 17:14:33 +0000 (17:14 +0000)
committerTom Lane
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 @@
 
 
  
@@ -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;
 
@@ -1714,27 +1715,43 @@ SRF_RETURN_DONE(funcctx)
      to clean up and end the SRF.
     
 
+    
+     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
+     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
+     multi_call_memory_ctx while doing the first-call setup.
+    
+
     
      A complete pseudo-code example looks like the following:
 
 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->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->max_calls = PG_GETARG_UINT32(0);
@@ -1800,6 +1822,8 @@ testpassbyval(PG_FUNCTION_ARGS)
          */
         attinmeta = TupleDescGetAttInMetadata(tupdesc);
         funcctx->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 .
@@ -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;
  *     
  * 
- *     if(SRF_IS_FIRSTCALL())
+ *     if (SRF_IS_FIRSTCALL())
  *     {
- *         
  *         funcctx = SRF_FIRSTCALL_INIT();
+ *     // switch context when allocating stuff to be used in later calls
+ *     oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
+ *         
  *         
  *             
  *             funcctx->slot = slot;
  *         
  *         
+ *     // return to original context when allocating transient memory
+ *     MemoryContextSwitchTo(oldcontext);
  *  }
  *     
  *     funcctx = SRF_PERCALL_SETUP();