fix memory usage in SRF

This commit is contained in:
wuchenglin
2023-03-21 11:29:33 +08:00
parent 3ba8f21d00
commit 3ef3d851c8
4 changed files with 54 additions and 12 deletions

View File

@ -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;

View File

@ -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 */
}

View File

@ -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);

View File

@ -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;
/* ----------------