From e4186762ffaa4188e16702e8f4f299ea70988b96 Mon Sep 17 00:00:00 2001 From: Tom Lane 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 804c37c6a9..42a7fc2165 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 a357f6f412..4fbc60bcf2 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; + /* + * Build a tuple description for a pgstattupe_type tuple + */ + tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE); - /* 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; - } + /* allocate a slot for a tuple with this tupdesc */ + slot = TupleDescGetSlot(tupdesc); - /* 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; - - /* 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 7c661a8ee3..d3370fb830 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 d05fc1a76b..37a6e723a6 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 b3f653a28a..fad7ad888d 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -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]); diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index 381b6047bf..d58d312238 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 83d0d1051d..199efbacd2 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 28311c26b7..35ba972fe1 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 660cd124ba..5114fcc38f 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 . @@ -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 27dbdf20e6..fcfb6acb69 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; * * - * 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();