Commit c954d490 authored by Jeff Davis's avatar Jeff Davis

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
parent 3ed2005f
...@@ -79,7 +79,8 @@ static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest, ...@@ -79,7 +79,8 @@ static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate, static void ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
ExprEvalStep *scratch, ExprEvalStep *scratch,
FunctionCallInfo fcinfo, AggStatePerTrans pertrans, 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, ...@@ -2924,10 +2925,13 @@ ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
* check for filters, evaluate aggregate input, check that that input is not * check for filters, evaluate aggregate input, check that that input is not
* NULL for a strict transition function, and then finally invoke the * NULL for a strict transition function, and then finally invoke the
* transition for each of the concurrently computed grouping sets. * 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 * ExprState *
ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase, ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
bool doSort, bool doHash) bool doSort, bool doHash, bool nullcheck)
{ {
ExprState *state = makeNode(ExprState); ExprState *state = makeNode(ExprState);
PlanState *parent = &aggstate->ss.ps; PlanState *parent = &aggstate->ss.ps;
...@@ -3158,7 +3162,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase, ...@@ -3158,7 +3162,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
for (int setno = 0; setno < processGroupingSets; setno++) for (int setno = 0; setno < processGroupingSets; setno++)
{ {
ExecBuildAggTransCall(state, aggstate, &scratch, trans_fcinfo, ExecBuildAggTransCall(state, aggstate, &scratch, trans_fcinfo,
pertrans, transno, setno, setoff, false); pertrans, transno, setno, setoff, false,
nullcheck);
setoff++; setoff++;
} }
} }
...@@ -3177,7 +3182,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase, ...@@ -3177,7 +3182,8 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
for (int setno = 0; setno < numHashes; setno++) for (int setno = 0; setno < numHashes; setno++)
{ {
ExecBuildAggTransCall(state, aggstate, &scratch, trans_fcinfo, ExecBuildAggTransCall(state, aggstate, &scratch, trans_fcinfo,
pertrans, transno, setno, setoff, true); pertrans, transno, setno, setoff, true,
nullcheck);
setoff++; setoff++;
} }
} }
...@@ -3227,15 +3233,28 @@ static void ...@@ -3227,15 +3233,28 @@ static void
ExecBuildAggTransCall(ExprState *state, AggState *aggstate, ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
ExprEvalStep *scratch, ExprEvalStep *scratch,
FunctionCallInfo fcinfo, AggStatePerTrans pertrans, FunctionCallInfo fcinfo, AggStatePerTrans pertrans,
int transno, int setno, int setoff, bool ishash) int transno, int setno, int setoff, bool ishash,
bool nullcheck)
{ {
ExprContext *aggcontext; ExprContext *aggcontext;
int adjust_jumpnull = -1;
if (ishash) if (ishash)
aggcontext = aggstate->hashcontext; aggcontext = aggstate->hashcontext;
else else
aggcontext = aggstate->aggcontexts[setno]; 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. * Determine appropriate transition implementation.
* *
...@@ -3303,6 +3322,16 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate, ...@@ -3303,6 +3322,16 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate,
scratch->d.agg_trans.transno = transno; scratch->d.agg_trans.transno = transno;
scratch->d.agg_trans.aggcontext = aggcontext; scratch->d.agg_trans.aggcontext = aggcontext;
ExprEvalPushStep(state, scratch); 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;
}
} }
/* /*
......
...@@ -435,6 +435,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) ...@@ -435,6 +435,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
&&CASE_EEOP_AGG_DESERIALIZE, &&CASE_EEOP_AGG_DESERIALIZE,
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS, &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
&&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS, &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
&&CASE_EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
&&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL, &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
&&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL, &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
&&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL, &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
...@@ -1603,6 +1604,22 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) ...@@ -1603,6 +1604,22 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
EEO_NEXT(); EEO_NEXT();
} }
/*
* Check for a NULL pointer to the per-group states.
*/
EEO_CASE(EEOP_AGG_PLAIN_PERGROUP_NULLCHECK)
{
AggState *aggstate = castNode(AggState, state->parent);
AggStatePerGroup pergroup_allaggs = aggstate->all_pergroups
[op->d.agg_plain_pergroup_nullcheck.setoff];
if (pergroup_allaggs == NULL)
EEO_JUMP(op->d.agg_plain_pergroup_nullcheck.jumpnull);
EEO_NEXT();
}
/* /*
* Different types of aggregate transition functions are implemented * Different types of aggregate transition functions are implemented
* as different types of steps, to avoid incurring unnecessary * as different types of steps, to avoid incurring unnecessary
......
...@@ -2928,7 +2928,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) ...@@ -2928,7 +2928,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
else else
Assert(false); Assert(false);
phase->evaltrans = ExecBuildAggTrans(aggstate, phase, dosort, dohash); phase->evaltrans = ExecBuildAggTrans(aggstate, phase, dosort, dohash,
false);
} }
......
...@@ -2046,6 +2046,45 @@ llvm_compile_expr(ExprState *state) ...@@ -2046,6 +2046,45 @@ llvm_compile_expr(ExprState *state)
break; break;
} }
case EEOP_AGG_PLAIN_PERGROUP_NULLCHECK:
{
int jumpnull;
LLVMValueRef v_aggstatep;
LLVMValueRef v_allpergroupsp;
LLVMValueRef v_pergroup_allaggs;
LLVMValueRef v_setoff;
jumpnull = op->d.agg_plain_pergroup_nullcheck.jumpnull;
/*
* pergroup_allaggs = aggstate->all_pergroups
* [op->d.agg_plain_pergroup_nullcheck.setoff];
*/
v_aggstatep = LLVMBuildBitCast(
b, v_parent, l_ptr(StructAggState), "");
v_allpergroupsp = l_load_struct_gep(
b, v_aggstatep,
FIELDNO_AGGSTATE_ALL_PERGROUPS,
"aggstate.all_pergroups");
v_setoff = l_int32_const(
op->d.agg_plain_pergroup_nullcheck.setoff);
v_pergroup_allaggs = l_load_gep1(
b, v_allpergroupsp, v_setoff, "");
LLVMBuildCondBr(
b,
LLVMBuildICmp(b, LLVMIntEQ,
LLVMBuildPtrToInt(
b, v_pergroup_allaggs, TypeSizeT, ""),
l_sizet_const(0), ""),
opblocks[jumpnull],
opblocks[opno + 1]);
break;
}
case EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL: case EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL:
case EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL: case EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL:
case EEOP_AGG_PLAIN_TRANS_BYVAL: case EEOP_AGG_PLAIN_TRANS_BYVAL:
......
...@@ -225,6 +225,7 @@ typedef enum ExprEvalOp ...@@ -225,6 +225,7 @@ typedef enum ExprEvalOp
EEOP_AGG_DESERIALIZE, EEOP_AGG_DESERIALIZE,
EEOP_AGG_STRICT_INPUT_CHECK_ARGS, EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
EEOP_AGG_STRICT_INPUT_CHECK_NULLS, EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL, EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL, EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
EEOP_AGG_PLAIN_TRANS_BYVAL, EEOP_AGG_PLAIN_TRANS_BYVAL,
...@@ -622,6 +623,13 @@ typedef struct ExprEvalStep ...@@ -622,6 +623,13 @@ typedef struct ExprEvalStep
int jumpnull; int jumpnull;
} agg_strict_input_check; } agg_strict_input_check;
/* for EEOP_AGG_PLAIN_PERGROUP_NULLCHECK */
struct
{
int setoff;
int jumpnull;
} agg_plain_pergroup_nullcheck;
/* for EEOP_AGG_PLAIN_TRANS_[INIT_][STRICT_]{BYVAL,BYREF} */ /* for EEOP_AGG_PLAIN_TRANS_[INIT_][STRICT_]{BYVAL,BYREF} */
/* for EEOP_AGG_ORDERED_TRANS_{DATUM,TUPLE} */ /* for EEOP_AGG_ORDERED_TRANS_{DATUM,TUPLE} */
struct struct
......
...@@ -255,7 +255,7 @@ extern ExprState *ExecInitQual(List *qual, PlanState *parent); ...@@ -255,7 +255,7 @@ extern ExprState *ExecInitQual(List *qual, PlanState *parent);
extern ExprState *ExecInitCheck(List *qual, PlanState *parent); extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
extern List *ExecInitExprList(List *nodes, PlanState *parent); extern List *ExecInitExprList(List *nodes, PlanState *parent);
extern ExprState *ExecBuildAggTrans(AggState *aggstate, struct AggStatePerPhaseData *phase, extern ExprState *ExecBuildAggTrans(AggState *aggstate, struct AggStatePerPhaseData *phase,
bool doSort, bool doHash); bool doSort, bool doHash, bool nullcheck);
extern ExprState *ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc, extern ExprState *ExecBuildGroupingEqual(TupleDesc ldesc, TupleDesc rdesc,
const TupleTableSlotOps *lops, const TupleTableSlotOps *rops, const TupleTableSlotOps *lops, const TupleTableSlotOps *rops,
int numCols, int numCols,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment