1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-08 11:42:09 +03:00

Extend ExecBuildAggTrans() to support a NULL pointer check.

Optionally push a step to check for a NULL pointer to the pergroup
state.

This will be important for disk-based hash aggregation in combination
with grouping sets. When memory limits are reached, a given tuple may
find its per-group state for some grouping sets but not others. For
the former, it advances the per-group state as normal; for the latter,
it skips evaluation and the calling code will have to spill the tuple
and reprocess it in a later batch.

Add the NULL check as a separate expression step because in some
common cases it's not needed.

Discussion: https://postgr.es/m/20200221202212.ssb2qpmdgrnx52sj%40alap3.anarazel.de
This commit is contained in:
Jeff Davis
2020-03-04 17:20:20 -08:00
parent 3ed2005ff5
commit c954d49046
6 changed files with 101 additions and 7 deletions

View File

@ -79,7 +79,8 @@ static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
ExprEvalStep *scratch,
FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
int transno, int setno, int setoff, bool ishash);
int transno, int setno, int setoff, bool ishash,
bool nullcheck);
/*
@ -2924,10 +2925,13 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
* check for filters, evaluate aggregate input, check that that input is not
* NULL for a strict transition function, and then finally invoke the
* transition for each of the concurrently computed grouping sets.
*
* If nullcheck is true, the generated code will check for a NULL pointer to
* the array of AggStatePerGroup, and skip evaluation if so.
*/
ExprState *
ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
bool doSort, bool doHash)
bool doSort, bool doHash, bool nullcheck)
{
ExprState *state = makeNode(ExprState);
PlanState *parent = &aggstate->ss.ps;
@ -3158,7 +3162,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
for (int setno = 0; setno < processGroupingSets; setno++)
{
ExecBuildAggTransCall(state, aggstate, &scratch, trans_fcinfo,
pertrans, transno, setno, setoff, false);
pertrans, transno, setno, setoff, false,
nullcheck);
setoff++;
}
}
@ -3177,7 +3182,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
for (int setno = 0; setno < numHashes; setno++)
{
ExecBuildAggTransCall(state, aggstate, &scratch, trans_fcinfo,
pertrans, transno, setno, setoff, true);
pertrans, transno, setno, setoff, true,
nullcheck);
setoff++;
}
}
@ -3227,15 +3233,28 @@ static void
ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
ExprEvalStep *scratch,
FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
int transno, int setno, int setoff, bool ishash)
int transno, int setno, int setoff, bool ishash,
bool nullcheck)
{
ExprContext *aggcontext;
int adjust_jumpnull = -1;
if (ishash)
aggcontext = aggstate->hashcontext;
else
aggcontext = aggstate->aggcontexts[setno];
/* add check for NULL pointer? */
if (nullcheck)
{
scratch->opcode = EEOP_AGG_PLAIN_PERGROUP_NULLCHECK;
scratch->d.agg_plain_pergroup_nullcheck.setoff = setoff;
/* adjust later */
scratch->d.agg_plain_pergroup_nullcheck.jumpnull = -1;
ExprEvalPushStep(state, scratch);
adjust_jumpnull = state->steps_len - 1;
}
/*
* Determine appropriate transition implementation.
*
@ -3303,6 +3322,16 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
scratch->d.agg_trans.transno = transno;
scratch->d.agg_trans.aggcontext = aggcontext;
ExprEvalPushStep(state, scratch);
/* fix up jumpnull */
if (adjust_jumpnull != -1)
{
ExprEvalStep *as = &state->steps[adjust_jumpnull];
Assert(as->opcode == EEOP_AGG_PLAIN_PERGROUP_NULLCHECK);
Assert(as->d.agg_plain_pergroup_nullcheck.jumpnull == -1);
as->d.agg_plain_pergroup_nullcheck.jumpnull = state->steps_len;
}
}
/*