Skip to content

Commit 45b0f35

Browse files
committed
Avoid leaking memory while evaluating arguments for a table function.
ExecMakeTableFunctionResult evaluated the arguments for a function-in-FROM in the query-lifespan memory context. This is insignificant in simple cases where the function relation is scanned only once; but if the function is in a sub-SELECT or is on the inside of a nested loop, any memory consumed during argument evaluation can add up quickly. (The potential for trouble here had been foreseen long ago, per existing comments; but we'd not previously seen a complaint from the field about it.) To fix, create an additional temporary context just for this purpose. Per an example from MauMau. Back-patch to all active branches.
1 parent 686f362 commit 45b0f35

File tree

4 files changed

+30
-4
lines changed

4 files changed

+30
-4
lines changed

src/backend/executor/execQual.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2002,6 +2002,7 @@ ExecMakeFunctionResultNoSets(FuncExprState *fcache,
20022002
Tuplestorestate *
20032003
ExecMakeTableFunctionResult(ExprState *funcexpr,
20042004
ExprContext *econtext,
2005+
MemoryContext argContext,
20052006
TupleDesc expectedDesc,
20062007
bool randomAccess)
20072008
{
@@ -2083,12 +2084,18 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
20832084
/*
20842085
* Evaluate the function's argument list.
20852086
*
2086-
* Note: ideally, we'd do this in the per-tuple context, but then the
2087-
* argument values would disappear when we reset the context in the
2088-
* inner loop. So do it in caller context. Perhaps we should make a
2089-
* separate context just to hold the evaluated arguments?
2087+
* We can't do this in the per-tuple context: the argument values
2088+
* would disappear when we reset that context in the inner loop. And
2089+
* the caller's CurrentMemoryContext is typically a query-lifespan
2090+
* context, so we don't want to leak memory there. We require the
2091+
* caller to pass a separate memory context that can be used for this,
2092+
* and can be reset each time through to avoid bloat.
20902093
*/
2094+
MemoryContextReset(argContext);
2095+
oldcontext = MemoryContextSwitchTo(argContext);
20912096
argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
2097+
MemoryContextSwitchTo(oldcontext);
2098+
20922099
/* We don't allow sets in the arguments of the table function */
20932100
if (argDone != ExprSingleResult)
20942101
ereport(ERROR,

src/backend/executor/nodeFunctionscan.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "nodes/nodeFuncs.h"
2929
#include "parser/parsetree.h"
3030
#include "utils/builtins.h"
31+
#include "utils/memutils.h"
3132

3233

3334
/*
@@ -94,6 +95,7 @@ FunctionNext(FunctionScanState *node)
9495
node->funcstates[0].tstore = tstore =
9596
ExecMakeTableFunctionResult(node->funcstates[0].funcexpr,
9697
node->ss.ps.ps_ExprContext,
98+
node->argcontext,
9799
node->funcstates[0].tupdesc,
98100
node->eflags & EXEC_FLAG_BACKWARD);
99101

@@ -152,6 +154,7 @@ FunctionNext(FunctionScanState *node)
152154
fs->tstore =
153155
ExecMakeTableFunctionResult(fs->funcexpr,
154156
node->ss.ps.ps_ExprContext,
157+
node->argcontext,
155158
fs->tupdesc,
156159
node->eflags & EXEC_FLAG_BACKWARD);
157160

@@ -515,6 +518,19 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
515518
ExecAssignResultTypeFromTL(&scanstate->ss.ps);
516519
ExecAssignScanProjectionInfo(&scanstate->ss);
517520

521+
/*
522+
* Create a memory context that ExecMakeTableFunctionResult can use to
523+
* evaluate function arguments in. We can't use the per-tuple context for
524+
* this because it gets reset too often; but we don't want to leak
525+
* evaluation results into the query-lifespan context either. We just
526+
* need one context, because we evaluate each function separately.
527+
*/
528+
scanstate->argcontext = AllocSetContextCreate(CurrentMemoryContext,
529+
"Table function arguments",
530+
ALLOCSET_DEFAULT_MINSIZE,
531+
ALLOCSET_DEFAULT_INITSIZE,
532+
ALLOCSET_DEFAULT_MAXSIZE);
533+
518534
return scanstate;
519535
}
520536

src/include/executor/executor.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
231231
bool *isNull);
232232
extern Tuplestorestate *ExecMakeTableFunctionResult(ExprState *funcexpr,
233233
ExprContext *econtext,
234+
MemoryContext argContext,
234235
TupleDesc expectedDesc,
235236
bool randomAccess);
236237
extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,

src/include/nodes/execnodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,7 @@ typedef struct SubqueryScanState
14071407
* nfuncs number of functions being executed
14081408
* funcstates per-function execution states (private in
14091409
* nodeFunctionscan.c)
1410+
* argcontext memory context to evaluate function arguments in
14101411
* ----------------
14111412
*/
14121413
struct FunctionScanPerFuncState;
@@ -1421,6 +1422,7 @@ typedef struct FunctionScanState
14211422
int nfuncs;
14221423
struct FunctionScanPerFuncState *funcstates; /* array of length
14231424
* nfuncs */
1425+
MemoryContext argcontext;
14241426
} FunctionScanState;
14251427

14261428
/* ----------------

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy