diff --git a/contrib/pgstattuple/README.pgstattuple b/contrib/pgstattuple/README.pgstattuple index 804c37c6a91a367bc658688027aa15e430f8c669..42a7fc2165dddcb6c81d9c9909d78639cf900333 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 a357f6f412ad26f598a52b88232fb87b14471a7b..4fbc60bcf2aa4a0bc1a03e89ac63b56150a9efc6 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 7c661a8ee3c21d5b3bfef624a137d477f9f21e46..d3370fb830cda48588ef3ef7d97004e92a91bb2c 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 d05fc1a76b66c04444218e3fb8689b6b9ccb038c..37a6e723a65862228717d1b62e3acfde5664d665 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 b3f653a28a12fc59a1770e23875188a95068c514..fad7ad888d8931c2e440cf3cb2ac7bfc565783e1 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->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]); diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index 381b6047bf0729b022cd583d385544b50abac2df..d58d312238e66d4fd8c99a45cf9d5630ebfd54e8 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 83d0d1051df974056d7122a049f1c79762f0ebb8..199efbacd26bdfdd20d88480f287db8eb5b51396 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 28311c26b7b2ec3db8901244afb10d4d8beb7bc0..35ba972fe12051d536fc2dc1a384c06ff2a5300c 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 660cd124ba90c6afec74c5bb34a5d760109af516..5114fcc38f29d5745e0a7067b169eb1f7fac1127 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 27dbdf20e62061a9207d0bee6ca64ad464807733..fcfb6acb69448a7f9a25698447ee8326ede4fdba 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();