Skip to content

Commit 313de22

Browse files
committed
SQL functions returning pass-by-reference types were copying the results
into the wrong memory context, resulting in a query-lifespan memory leak. Bug is new in 8.0, I believe. Per report from Rae Stiening.
1 parent 9427cce commit 313de22

File tree

1 file changed

+22
-13
lines changed

1 file changed

+22
-13
lines changed

src/backend/executor/functions.c

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.96 2005/04/06 16:34:04 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.97 2005/04/10 18:04:20 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -86,12 +86,13 @@ static execution_state *init_execution_state(List *queryTree_list,
8686
static void init_sql_fcache(FmgrInfo *finfo);
8787
static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
8888
static TupleTableSlot *postquel_getnext(execution_state *es);
89-
static void postquel_end(execution_state *es, SQLFunctionCachePtr fcache);
89+
static void postquel_end(execution_state *es);
9090
static void postquel_sub_params(SQLFunctionCachePtr fcache,
9191
FunctionCallInfo fcinfo);
9292
static Datum postquel_execute(execution_state *es,
9393
FunctionCallInfo fcinfo,
94-
SQLFunctionCachePtr fcache);
94+
SQLFunctionCachePtr fcache,
95+
MemoryContext resultcontext);
9596
static void sql_exec_error_callback(void *arg);
9697
static void ShutdownSQLFunction(Datum arg);
9798

@@ -384,7 +385,7 @@ postquel_getnext(execution_state *es)
384385
}
385386

386387
static void
387-
postquel_end(execution_state *es, SQLFunctionCachePtr fcache)
388+
postquel_end(execution_state *es)
388389
{
389390
Snapshot saveActiveSnapshot;
390391

@@ -454,10 +455,12 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
454455
static Datum
455456
postquel_execute(execution_state *es,
456457
FunctionCallInfo fcinfo,
457-
SQLFunctionCachePtr fcache)
458+
SQLFunctionCachePtr fcache,
459+
MemoryContext resultcontext)
458460
{
459461
TupleTableSlot *slot;
460462
Datum value;
463+
MemoryContext oldcontext;
461464

462465
if (es->status == F_EXEC_START)
463466
postquel_start(es, fcache);
@@ -470,7 +473,7 @@ postquel_execute(execution_state *es,
470473
* We fall out here for all cases except where we have obtained
471474
* a row from a function's final SELECT.
472475
*/
473-
postquel_end(es, fcache);
476+
postquel_end(es);
474477
fcinfo->isnull = true;
475478
return (Datum) NULL;
476479
}
@@ -482,8 +485,12 @@ postquel_execute(execution_state *es,
482485
Assert(LAST_POSTQUEL_COMMAND(es));
483486

484487
/*
485-
* Set up to return the function value.
488+
* Set up to return the function value. For pass-by-reference
489+
* datatypes, be sure to allocate the result in resultcontext,
490+
* not the current memory context (which has query lifespan).
486491
*/
492+
oldcontext = MemoryContextSwitchTo(resultcontext);
493+
487494
if (fcache->returnsTuple)
488495
{
489496
/*
@@ -492,7 +499,7 @@ postquel_execute(execution_state *es,
492499
* reasons why we do this:
493500
*
494501
* 1. To copy the tuple out of the child execution context and
495-
* into our own context.
502+
* into the desired result context.
496503
*
497504
* 2. To remove any junk attributes present in the raw subselect
498505
* result. (This is probably not absolutely necessary, but it
@@ -553,21 +560,23 @@ postquel_execute(execution_state *es,
553560
{
554561
/*
555562
* Returning a scalar, which we have to extract from the first
556-
* column of the SELECT result, and then copy into current
557-
* execution context if needed.
563+
* column of the SELECT result, and then copy into result
564+
* context if needed.
558565
*/
559566
value = slot_getattr(slot, 1, &(fcinfo->isnull));
560567

561568
if (!fcinfo->isnull)
562569
value = datumCopy(value, fcache->typbyval, fcache->typlen);
563570
}
564571

572+
MemoryContextSwitchTo(oldcontext);
573+
565574
/*
566575
* If this is a single valued function we have to end the function
567576
* execution now.
568577
*/
569578
if (!fcinfo->flinfo->fn_retset)
570-
postquel_end(es, fcache);
579+
postquel_end(es);
571580

572581
return value;
573582
}
@@ -627,7 +636,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
627636
*/
628637
while (es)
629638
{
630-
result = postquel_execute(es, fcinfo, fcache);
639+
result = postquel_execute(es, fcinfo, fcache, oldcontext);
631640
if (es->status != F_EXEC_DONE)
632641
break;
633642
es = es->next;
@@ -824,7 +833,7 @@ ShutdownSQLFunction(Datum arg)
824833
{
825834
/* Shut down anything still running */
826835
if (es->status == F_EXEC_RUN)
827-
postquel_end(es, fcache);
836+
postquel_end(es);
828837
/* Reset states to START in case we're called again */
829838
es->status = F_EXEC_START;
830839
es = es->next;

0 commit comments

Comments
 (0)