Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit e418676

Browse files
committed
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.
1 parent 0201dac commit e418676

File tree

10 files changed

+154
-134
lines changed

10 files changed

+154
-134
lines changed

contrib/pgstattuple/README.pgstattuple

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
pgstattuple README 2002/08/22 Tatsuo Ishii
1+
pgstattuple README 2002/08/29 Tatsuo Ishii
22

33
1. What is pgstattuple?
44

@@ -40,15 +40,15 @@ free_percent -- free space in %
4040

4141
3. Using pgstattuple
4242

43-
pgstattuple may be called as a SRF (set returning function) and is
43+
pgstattuple may be called as a table function and is
4444
defined as follows:
4545

46-
CREATE OR REPLACE FUNCTION pgstattuple(text) RETURNS SETOF pgstattuple_view
46+
CREATE OR REPLACE FUNCTION pgstattuple(text) RETURNS pgstattuple_type
4747
AS 'MODULE_PATHNAME', 'pgstattuple'
4848
LANGUAGE 'c' WITH (isstrict);
4949

50-
The argument is the table name. Note that pgstattuple never
51-
returns more than 1 tuple.
50+
The argument is the table name. Note that pgstattuple only returns
51+
one row.
5252

5353
4. Notes
5454

contrib/pgstattuple/pgstattuple.c

Lines changed: 23 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.7 2002/08/23 08:19:49 ishii Exp $
2+
* $Header: /cvsroot/pgsql/contrib/pgstattuple/pgstattuple.c,v 1.8 2002/08/29 17:14:31 tgl Exp $
33
*
44
* Copyright (c) 2001,2002 Tatsuo Ishii
55
*
@@ -25,10 +25,10 @@
2525
#include "postgres.h"
2626

2727
#include "fmgr.h"
28+
#include "funcapi.h"
2829
#include "access/heapam.h"
2930
#include "access/transam.h"
3031
#include "catalog/namespace.h"
31-
#include "funcapi.h"
3232
#include "utils/builtins.h"
3333

3434

@@ -41,19 +41,19 @@ extern Datum pgstattuple(PG_FUNCTION_ARGS);
4141
* returns live/dead tuples info
4242
*
4343
* C FUNCTION definition
44-
* pgstattuple(TEXT) returns setof pgstattuple_view
45-
* see pgstattuple.sql for pgstattuple_view
44+
* pgstattuple(text) returns pgstattuple_type
45+
* see pgstattuple.sql for pgstattuple_type
4646
* ----------
4747
*/
4848

49-
#define DUMMY_TUPLE "pgstattuple_view"
49+
#define DUMMY_TUPLE "pgstattuple_type"
5050
#define NCOLUMNS 9
5151
#define NCHARS 32
5252

5353
Datum
5454
pgstattuple(PG_FUNCTION_ARGS)
5555
{
56-
text *relname;
56+
text *relname = PG_GETARG_TEXT_P(0);
5757
RangeVar *relrv;
5858
Relation rel;
5959
HeapScanDesc scan;
@@ -71,62 +71,30 @@ pgstattuple(PG_FUNCTION_ARGS)
7171
double dead_tuple_percent;
7272
uint64 free_space = 0; /* free/reusable space in bytes */
7373
double free_percent; /* free/reusable space in % */
74-
75-
FuncCallContext *funcctx;
76-
int call_cntr;
77-
int max_calls;
7874
TupleDesc tupdesc;
7975
TupleTableSlot *slot;
8076
AttInMetadata *attinmeta;
77+
char **values;
78+
int i;
79+
Datum result;
8180

82-
char **values;
83-
int i;
84-
Datum result;
85-
86-
/* stuff done only on the first call of the function */
87-
if(SRF_IS_FIRSTCALL())
88-
{
89-
/* create a function context for cross-call persistence */
90-
funcctx = SRF_FIRSTCALL_INIT();
91-
92-
/* total number of tuples to be returned */
93-
funcctx->max_calls = 1;
94-
95-
/*
96-
* Build a tuple description for a pgstattupe_view tuple
97-
*/
98-
tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE);
99-
100-
/* allocate a slot for a tuple with this tupdesc */
101-
slot = TupleDescGetSlot(tupdesc);
102-
103-
/* assign slot to function context */
104-
funcctx->slot = slot;
105-
106-
/*
107-
* Generate attribute metadata needed later to produce tuples from raw
108-
* C strings
109-
*/
110-
attinmeta = TupleDescGetAttInMetadata(tupdesc);
111-
funcctx->attinmeta = attinmeta;
112-
}
81+
/*
82+
* Build a tuple description for a pgstattupe_type tuple
83+
*/
84+
tupdesc = RelationNameGetTupleDesc(DUMMY_TUPLE);
11385

114-
/* stuff done on every call of the function */
115-
funcctx = SRF_PERCALL_SETUP();
116-
call_cntr = funcctx->call_cntr;
117-
max_calls = funcctx->max_calls;
118-
slot = funcctx->slot;
119-
attinmeta = funcctx->attinmeta;
86+
/* allocate a slot for a tuple with this tupdesc */
87+
slot = TupleDescGetSlot(tupdesc);
12088

121-
/* Are we done? */
122-
if (call_cntr >= max_calls)
123-
{
124-
SRF_RETURN_DONE(funcctx);
125-
}
89+
/*
90+
* Generate attribute metadata needed later to produce tuples from raw
91+
* C strings
92+
*/
93+
attinmeta = TupleDescGetAttInMetadata(tupdesc);
12694

12795
/* open relation */
128-
relname = PG_GETARG_TEXT_P(0);
129-
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,"pgstattuple"));
96+
relrv = makeRangeVarFromNameList(textToQualifiedNameList(relname,
97+
"pgstattuple"));
13098
rel = heap_openrv(relrv, AccessShareLock);
13199

132100
nblocks = RelationGetNumberOfBlocks(rel);
@@ -223,5 +191,5 @@ pgstattuple(PG_FUNCTION_ARGS)
223191
}
224192
pfree(values);
225193

226-
SRF_RETURN_NEXT(funcctx, result);
194+
PG_RETURN_DATUM(result);
227195
}
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
DROP VIEW pgstattuple_view CASCADE;
2-
CREATE VIEW pgstattuple_view AS
3-
SELECT
4-
0::BIGINT AS table_len, -- physical table length in bytes
5-
0::BIGINT AS tuple_count, -- number of live tuples
6-
0::BIGINT AS tuple_len, -- total tuples length in bytes
7-
0.0::FLOAT AS tuple_percent, -- live tuples in %
8-
0::BIGINT AS dead_tuple_count, -- number of dead tuples
9-
0::BIGINT AS dead_tuple_len, -- total dead tuples length in bytes
10-
0.0::FLOAT AS dead_tuple_percent, -- dead tuples in %
11-
0::BIGINT AS free_space, -- free space in bytes
12-
0.0::FLOAT AS free_percent; -- free space in %
1+
DROP TYPE pgstattuple_type CASCADE;
2+
CREATE TYPE pgstattuple_type AS (
3+
table_len BIGINT, -- physical table length in bytes
4+
tuple_count BIGINT, -- number of live tuples
5+
tuple_len BIGINT, -- total tuples length in bytes
6+
tuple_percent FLOAT, -- live tuples in %
7+
dead_tuple_count BIGINT, -- number of dead tuples
8+
dead_tuple_len BIGINT, -- total dead tuples length in bytes
9+
dead_tuple_percent FLOAT, -- dead tuples in %
10+
free_space BIGINT, -- free space in bytes
11+
free_percent FLOAT -- free space in %
12+
);
1313

14-
CREATE OR REPLACE FUNCTION pgstattuple(text) RETURNS SETOF pgstattuple_view
14+
CREATE OR REPLACE FUNCTION pgstattuple(text) RETURNS pgstattuple_type
1515
AS 'MODULE_PATHNAME', 'pgstattuple'
1616
LANGUAGE 'c' WITH (isstrict);

contrib/tablefunc/tablefunc.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,17 @@ normal_rand(PG_FUNCTION_ARGS)
8787
float8 stddev;
8888
float8 carry_val;
8989
bool use_carry;
90+
MemoryContext oldcontext;
9091

9192
/* stuff done only on the first call of the function */
9293
if(SRF_IS_FIRSTCALL())
9394
{
9495
/* create a function context for cross-call persistence */
9596
funcctx = SRF_FIRSTCALL_INIT();
9697

98+
/* switch to memory context appropriate for multiple function calls */
99+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
100+
97101
/* total number of tuples to be returned */
98102
funcctx->max_calls = PG_GETARG_UINT32(0);
99103

@@ -119,6 +123,8 @@ normal_rand(PG_FUNCTION_ARGS)
119123
* purpose it doesn't matter, just cast it as an unsigned value
120124
*/
121125
srandom(PG_GETARG_UINT32(3));
126+
127+
MemoryContextSwitchTo(oldcontext);
122128
}
123129

124130
/* stuff done on every call of the function */
@@ -260,10 +266,11 @@ crosstab(PG_FUNCTION_ARGS)
260266
AttInMetadata *attinmeta;
261267
SPITupleTable *spi_tuptable = NULL;
262268
TupleDesc spi_tupdesc;
263-
char *lastrowid;
269+
char *lastrowid = NULL;
264270
crosstab_fctx *fctx;
265271
int i;
266272
int num_categories;
273+
MemoryContext oldcontext;
267274

268275
/* stuff done only on the first call of the function */
269276
if(SRF_IS_FIRSTCALL())
@@ -275,13 +282,12 @@ crosstab(PG_FUNCTION_ARGS)
275282
TupleDesc tupdesc = NULL;
276283
int ret;
277284
int proc;
278-
MemoryContext oldcontext;
279285

280286
/* create a function context for cross-call persistence */
281287
funcctx = SRF_FIRSTCALL_INIT();
282288

283-
/* SPI switches context on us, so save it first */
284-
oldcontext = CurrentMemoryContext;
289+
/* switch to memory context appropriate for multiple function calls */
290+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
285291

286292
/* Connect to SPI manager */
287293
if ((ret = SPI_connect()) < 0)
@@ -317,8 +323,8 @@ crosstab(PG_FUNCTION_ARGS)
317323
SRF_RETURN_DONE(funcctx);
318324
}
319325

320-
/* back to the original memory context */
321-
MemoryContextSwitchTo(oldcontext);
326+
/* SPI switches context on us, so reset it */
327+
MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
322328

323329
/* get the typeid that represents our return type */
324330
functypeid = get_func_rettype(funcid);
@@ -381,6 +387,8 @@ crosstab(PG_FUNCTION_ARGS)
381387

382388
/* total number of tuples to be returned */
383389
funcctx->max_calls = proc;
390+
391+
MemoryContextSwitchTo(oldcontext);
384392
}
385393

386394
/* stuff done on every call of the function */
@@ -432,7 +440,7 @@ crosstab(PG_FUNCTION_ARGS)
432440
for (i = 0; i < num_categories; i++)
433441
{
434442
HeapTuple spi_tuple;
435-
char *rowid;
443+
char *rowid = NULL;
436444

437445
/* see if we've gone too far already */
438446
if (call_cntr >= max_calls)
@@ -496,7 +504,13 @@ crosstab(PG_FUNCTION_ARGS)
496504
xpfree(fctx->lastrowid);
497505

498506
if (values[0] != NULL)
507+
{
508+
/* switch to memory context appropriate for multiple function calls */
509+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
510+
499511
lastrowid = fctx->lastrowid = pstrdup(values[0]);
512+
MemoryContextSwitchTo(oldcontext);
513+
}
500514

501515
if (!allnulls)
502516
{

doc/src/sgml/xfunc.sgml

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.57 2002/08/29 00:17:02 tgl Exp $
2+
$Header: /cvsroot/pgsql/doc/src/sgml/xfunc.sgml,v 1.58 2002/08/29 17:14:32 tgl Exp $
33
-->
44

55
<chapter id="xfunc">
@@ -1670,13 +1670,14 @@ typedef struct
16701670
AttInMetadata *attinmeta;
16711671

16721672
/*
1673-
* memory context used to initialize structure
1673+
* memory context used for structures which must live for multiple calls
16741674
*
1675-
* fmctx is set by SRF_FIRSTCALL_INIT() for you, and used by
1676-
* SRF_RETURN_DONE() for cleanup. It is primarily for internal use
1677-
* by the API.
1675+
* multi_call_memory_ctx is set by SRF_FIRSTCALL_INIT() for you, and used
1676+
* by SRF_RETURN_DONE() for cleanup. It is the most appropriate memory
1677+
* context for any memory that is to be re-used across multiple calls
1678+
* of the SRF.
16781679
*/
1679-
MemoryContext fmctx;
1680+
MemoryContext multi_call_memory_ctx;
16801681

16811682
} FuncCallContext;
16821683
</programlisting>
@@ -1714,27 +1715,43 @@ SRF_RETURN_DONE(funcctx)
17141715
to clean up and end the SRF.
17151716
</para>
17161717

1718+
<para>
1719+
The palloc memory context that is current when the SRF is called is
1720+
a transient context that will be cleared between calls. This means
1721+
that you do not need to be careful about pfree'ing everything
1722+
you palloc; it will go away anyway. However, if you want to allocate
1723+
any data structures to live across calls, you need to put them somewhere
1724+
else. The memory context referenced by
1725+
<structfield>multi_call_memory_ctx</> is a suitable location for any
1726+
data that needs to survive until the SRF is finished running. In most
1727+
cases, this means that you should switch into
1728+
<structfield>multi_call_memory_ctx</> while doing the first-call setup.
1729+
</para>
1730+
17171731
<para>
17181732
A complete pseudo-code example looks like the following:
17191733
<programlisting>
17201734
Datum
17211735
my_Set_Returning_Function(PG_FUNCTION_ARGS)
17221736
{
1723-
FuncCallContext *funcctx;
1724-
Datum result;
1737+
FuncCallContext *funcctx;
1738+
Datum result;
1739+
MemoryContext oldcontext;
17251740
[user defined declarations]
17261741

17271742
if (SRF_IS_FIRSTCALL())
17281743
{
1744+
funcctx = SRF_FIRSTCALL_INIT();
1745+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
17291746
/* one-time setup code appears here: */
17301747
[user defined code]
1731-
funcctx = SRF_FIRSTCALL_INIT();
17321748
[if returning composite]
17331749
[build TupleDesc, and perhaps AttInMetadata]
17341750
[obtain slot]
17351751
funcctx-&gt;slot = slot;
17361752
[endif returning composite]
17371753
[user defined code]
1754+
MemoryContextSwitchTo(oldcontext);
17381755
}
17391756

17401757
/* each-time setup code appears here: */
@@ -1777,8 +1794,13 @@ testpassbyval(PG_FUNCTION_ARGS)
17771794
/* stuff done only on the first call of the function */
17781795
if (SRF_IS_FIRSTCALL())
17791796
{
1797+
MemoryContext oldcontext;
1798+
17801799
/* create a function context for cross-call persistence */
1781-
funcctx = SRF_FIRSTCALL_INIT();
1800+
funcctx = SRF_FIRSTCALL_INIT();
1801+
1802+
/* switch to memory context appropriate for multiple function calls */
1803+
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
17821804

17831805
/* total number of tuples to be returned */
17841806
funcctx-&gt;max_calls = PG_GETARG_UINT32(0);
@@ -1800,6 +1822,8 @@ testpassbyval(PG_FUNCTION_ARGS)
18001822
*/
18011823
attinmeta = TupleDescGetAttInMetadata(tupdesc);
18021824
funcctx-&gt;attinmeta = attinmeta;
1825+
1826+
MemoryContextSwitchTo(oldcontext);
18031827
}
18041828

18051829
/* stuff done on every call of the function */
@@ -1836,7 +1860,7 @@ testpassbyval(PG_FUNCTION_ARGS)
18361860
/* make the tuple into a datum */
18371861
result = TupleGetDatum(slot, tuple);
18381862

1839-
/* Clean up */
1863+
/* Clean up (this is not actually necessary) */
18401864
pfree(values[0]);
18411865
pfree(values[1]);
18421866
pfree(values[2]);

0 commit comments

Comments
 (0)