fix memory usage in SRF
This commit is contained in:
@ -185,9 +185,14 @@ FuncExprState *ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, Plan
|
||||
* function itself. The argument expressions may not contain set-returning
|
||||
* functions (the planner is supposed to have separated evaluation for those).
|
||||
*
|
||||
* This should be called in a short-lived (per-tuple) context, argContext
|
||||
* needs to live until all rows have been returned (i.e. *isDone set to
|
||||
* ExprEndResult or ExprSingleResult).
|
||||
*
|
||||
* This is used by nodeProjectSet.c.
|
||||
*/
|
||||
Datum ExecMakeFunctionResultSet(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone)
|
||||
Datum ExecMakeFunctionResultSet(FuncExprState *fcache, ExprContext *econtext, MemoryContext argContext,
|
||||
bool *isNull, ExprDoneCond *isDone)
|
||||
{
|
||||
List *arguments;
|
||||
Datum result;
|
||||
@ -215,8 +220,19 @@ restart:
|
||||
*/
|
||||
if (fcache->funcResultStore)
|
||||
{
|
||||
TupleTableSlot *slot = fcache->funcResultSlot;
|
||||
MemoryContext oldContext;
|
||||
bool foundTup;
|
||||
econtext->hasSetResultStore = true;
|
||||
if (tuplestore_gettupleslot(fcache->funcResultStore, true, false, fcache->funcResultSlot)) {
|
||||
/*
|
||||
* Have to make sure tuple in slot lives long enough, otherwise
|
||||
* clearing the slot could end up trying to free something already
|
||||
* freed.
|
||||
*/
|
||||
oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
|
||||
foundTup = tuplestore_gettupleslot(fcache->funcResultStore, true, false, fcache->funcResultSlot);
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
if (foundTup) {
|
||||
*isDone = ExprMultipleResult;
|
||||
if (fcache->funcReturnsTuple) {
|
||||
/* We must return the whole tuple as a Datum. */
|
||||
@ -265,10 +281,12 @@ restart:
|
||||
|
||||
arguments = fcache->args;
|
||||
if (!fcache->setArgsValid) {
|
||||
MemoryContext oldContext = MemoryContextSwitchTo(argContext);
|
||||
if (has_refcursor)
|
||||
ExecEvalFuncArgs<true>(fcinfo, arguments, econtext, var_dno);
|
||||
else
|
||||
ExecEvalFuncArgs<false>(fcinfo, arguments, econtext);
|
||||
MemoryContextSwitchTo(oldContext);
|
||||
} else {
|
||||
/* Reset flag (we may set it again below) */
|
||||
fcache->setArgsValid = false;
|
||||
|
@ -99,6 +99,16 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
|
||||
off++;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a memory context that ExecMakeFunctionResult can use to evaluate
|
||||
* function arguments in. We can't use the per-tuple context for this
|
||||
* because it gets reset too often; but we don't want to leak evaluation
|
||||
* results into the query-lifespan context either. We use one context for
|
||||
* the arguments of all tSRFs, as they have roughly equivalent lifetimes.
|
||||
*/
|
||||
state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
|
||||
"tSRF function arguments", ALLOCSET_DEFAULT_SIZES);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@ -110,6 +120,13 @@ static TupleTableSlot *ExecProjectSet(PlanState *state)
|
||||
PlanState *outerPlan;
|
||||
ExprContext *econtext = node->ps.ps_ExprContext;
|
||||
|
||||
/*
|
||||
* Reset per-tuple context to free expression-evaluation storage allocated
|
||||
* for a potentially previously returned tuple. Note that the SRF argument
|
||||
* context has a different lifetime and is reset below.
|
||||
*/
|
||||
ResetExprContext(econtext);
|
||||
|
||||
CHECK_FOR_INTERRUPTS();
|
||||
|
||||
/*
|
||||
@ -125,11 +142,13 @@ static TupleTableSlot *ExecProjectSet(PlanState *state)
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset per-tuple memory context to free any expression evaluation
|
||||
* storage allocated in the previous tuple cycle. Note this can't happen
|
||||
* until we're done projecting out tuples from a scan tuple.
|
||||
* Reset argument context to free any expression evaluation storage
|
||||
* allocated in the previous tuple cycle. Note this can't happen until
|
||||
* we're done projecting out tuples from a scan tuple, as ValuePerCall
|
||||
* functions are allowed to reference the arguments for each returned
|
||||
* tuple.
|
||||
*/
|
||||
ResetExprContext(econtext);
|
||||
MemoryContextReset(node->argcontext);
|
||||
|
||||
/*
|
||||
* Get another input tuple and project SRFs from it.
|
||||
@ -166,15 +185,15 @@ static TupleTableSlot *ExecProjectSet(PlanState *state)
|
||||
}
|
||||
|
||||
static inline Datum
|
||||
execMakeExprResult(Node *arg, ExprContext *econtext, bool *isnull,
|
||||
ExprDoneCond *isdone, bool *hassrf)
|
||||
execMakeExprResult(Node *arg, ExprContext *econtext, MemoryContext argContext,
|
||||
bool *isnull, ExprDoneCond *isdone, bool *hassrf)
|
||||
{
|
||||
Datum result;
|
||||
if (IsA(arg, FuncExprState)) {
|
||||
/*
|
||||
* Evaluate SRF - possibly continuing previously started output.
|
||||
*/
|
||||
result = ExecMakeFunctionResultSet((FuncExprState*)arg, econtext, isnull, isdone);
|
||||
result = ExecMakeFunctionResultSet((FuncExprState*)arg, econtext, argContext, isnull, isdone);
|
||||
*hassrf = true;
|
||||
} else {
|
||||
/* Non-SRF tlist expression, just evaluate normally. */
|
||||
@ -233,7 +252,8 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node)
|
||||
|
||||
ELOG_FIELD_NAME_START(resname);
|
||||
|
||||
*result = execMakeExprResult(elem, econtext, isnull, itemIsDone, &hassrf);
|
||||
*result = execMakeExprResult(elem, econtext, node->argcontext,
|
||||
isnull, itemIsDone, &hassrf);
|
||||
|
||||
ELOG_FIELD_NAME_END;
|
||||
|
||||
@ -299,7 +319,8 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node)
|
||||
ELOG_FIELD_NAME_START(resname);
|
||||
|
||||
/*restart the done ones*/
|
||||
*result = ExecMakeFunctionResultSet((FuncExprState*)elem, econtext, isnull, itemIsDone);
|
||||
*result = ExecMakeFunctionResultSet((FuncExprState*)elem, econtext,
|
||||
node->argcontext, isnull, itemIsDone);
|
||||
|
||||
ELOG_FIELD_NAME_END;
|
||||
|
||||
@ -354,7 +375,8 @@ static TupleTableSlot *ExecProjectSRF(ProjectSetState *node)
|
||||
ELOG_FIELD_NAME_START(resname);
|
||||
|
||||
while (*itemIsDone == ExprMultipleResult) {
|
||||
*result = ExecMakeFunctionResultSet((FuncExprState*)elem, econtext, isnull, itemIsDone);
|
||||
*result = ExecMakeFunctionResultSet((FuncExprState*)elem, econtext,
|
||||
node->argcontext, isnull, itemIsDone);
|
||||
/* no need for MakeExpandedObjectReadOnly */
|
||||
}
|
||||
|
||||
|
@ -342,6 +342,7 @@ extern FuncExprState *ExecInitTableFunctionResult(Expr *expr, ExprContext *econt
|
||||
extern FuncExprState *ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent);
|
||||
extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache,
|
||||
ExprContext *econtext,
|
||||
MemoryContext argContext,
|
||||
bool *isNull,
|
||||
ExprDoneCond *isDone);
|
||||
|
||||
|
@ -1475,6 +1475,7 @@ typedef struct ProjectSetState {
|
||||
ExprDoneCond *elemdone; /* array of per-SRF is-done states */
|
||||
int nelems; /* length of elemdone[] array */
|
||||
bool pending_srf_tuples; /* still evaluating srfs in tlist? */
|
||||
MemoryContext argcontext; /* context for SRF arguments */
|
||||
} ProjectSetState;
|
||||
|
||||
/* ----------------
|
||||
|
Reference in New Issue
Block a user