diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c index 121eff97a0c..91aa386fa61 100644 --- a/src/backend/executor/execExpr.c +++ b/src/backend/executor/execExpr.c @@ -3229,8 +3229,6 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate, FunctionCallInfo fcinfo, AggStatePerTrans pertrans, int transno, int setno, int setoff, bool ishash) { - int adjust_init_jumpnull = -1; - int adjust_strict_jumpnull = -1; ExprContext *aggcontext; if (ishash) @@ -3239,52 +3237,61 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate, aggcontext = aggstate->aggcontexts[setno]; /* + * Determine appropriate transition implementation. + * + * For non-ordered aggregates: + * * If the initial value for the transition state doesn't exist in the * pg_aggregate table then we will let the first non-NULL value returned * from the outer procNode become the initial value. (This is useful for * aggregates like max() and min().) The noTransValue flag signals that we - * still need to do this. + * need to do so. If true, generate a + * EEOP_AGG_INIT_STRICT_PLAIN_TRANS{,_BYVAL} step. This step also needs to + * do the work described next: + * + * If the function is strict, but does have an initial value, choose + * EEOP_AGG_STRICT_PLAIN_TRANS{,_BYVAL}, which skips the transition + * function if the transition value has become NULL (because a previous + * transition function returned NULL). This step also needs to do the work + * described next: + * + * Otherwise we call EEOP_AGG_PLAIN_TRANS{,_BYVAL}, which does not have to + * perform either of the above checks. + * + * Having steps with overlapping responsibilities is not nice, but + * aggregations are very performance sensitive, making this worthwhile. + * + * For ordered aggregates: + * + * Only need to choose between the faster path for a single orderred + * column, and the one between multiple columns. Checking strictness etc + * is done when finalizing the aggregate. See + * process_ordered_aggregate_{single, multi} and + * advance_transition_function. */ - if (pertrans->numSortCols == 0 && - fcinfo->flinfo->fn_strict && - pertrans->initValueIsNull) + if (pertrans->numSortCols == 0) { - scratch->opcode = EEOP_AGG_INIT_TRANS; - scratch->d.agg_init_trans.pertrans = pertrans; - scratch->d.agg_init_trans.setno = setno; - scratch->d.agg_init_trans.setoff = setoff; - scratch->d.agg_init_trans.transno = transno; - scratch->d.agg_init_trans.aggcontext = aggcontext; - scratch->d.agg_init_trans.jumpnull = -1; /* adjust later */ - ExprEvalPushStep(state, scratch); - - /* see comment about jumping out below */ - adjust_init_jumpnull = state->steps_len - 1; + if (pertrans->transtypeByVal) + { + if (fcinfo->flinfo->fn_strict && + pertrans->initValueIsNull) + scratch->opcode = EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL; + else if (fcinfo->flinfo->fn_strict) + scratch->opcode = EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL; + else + scratch->opcode = EEOP_AGG_PLAIN_TRANS_BYVAL; + } + else + { + if (fcinfo->flinfo->fn_strict && + pertrans->initValueIsNull) + scratch->opcode = EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF; + else if (fcinfo->flinfo->fn_strict) + scratch->opcode = EEOP_AGG_PLAIN_TRANS_STRICT_BYREF; + else + scratch->opcode = EEOP_AGG_PLAIN_TRANS_BYREF; + } } - - if (pertrans->numSortCols == 0 && - fcinfo->flinfo->fn_strict) - { - scratch->opcode = EEOP_AGG_STRICT_TRANS_CHECK; - scratch->d.agg_strict_trans_check.setno = setno; - scratch->d.agg_strict_trans_check.setoff = setoff; - scratch->d.agg_strict_trans_check.transno = transno; - scratch->d.agg_strict_trans_check.jumpnull = -1; /* adjust later */ - ExprEvalPushStep(state, scratch); - - /* - * Note, we don't push into adjust_bailout here - those jump to the - * end of all transition value computations. Here a single transition - * value is NULL, so just skip processing the individual value. - */ - adjust_strict_jumpnull = state->steps_len - 1; - } - - /* invoke appropriate transition implementation */ - if (pertrans->numSortCols == 0 && pertrans->transtypeByVal) - scratch->opcode = EEOP_AGG_PLAIN_TRANS_BYVAL; - else if (pertrans->numSortCols == 0) - scratch->opcode = EEOP_AGG_PLAIN_TRANS; else if (pertrans->numInputs == 1) scratch->opcode = EEOP_AGG_ORDERED_TRANS_DATUM; else @@ -3296,22 +3303,6 @@ ExecBuildAggTransCall(ExprState *state, AggState *aggstate, scratch->d.agg_trans.transno = transno; scratch->d.agg_trans.aggcontext = aggcontext; ExprEvalPushStep(state, scratch); - - /* adjust jumps so they jump till after transition invocation */ - if (adjust_init_jumpnull != -1) - { - ExprEvalStep *as = &state->steps[adjust_init_jumpnull]; - - Assert(as->d.agg_init_trans.jumpnull == -1); - as->d.agg_init_trans.jumpnull = state->steps_len; - } - if (adjust_strict_jumpnull != -1) - { - ExprEvalStep *as = &state->steps[adjust_strict_jumpnull]; - - Assert(as->d.agg_strict_trans_check.jumpnull == -1); - as->d.agg_strict_trans_check.jumpnull = state->steps_len; - } } /* diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c index 35eb8b99f69..eafd4849002 100644 --- a/src/backend/executor/execExprInterp.c +++ b/src/backend/executor/execExprInterp.c @@ -166,6 +166,16 @@ static Datum ExecJustAssignInnerVarVirt(ExprState *state, ExprContext *econtext, static Datum ExecJustAssignOuterVarVirt(ExprState *state, ExprContext *econtext, bool *isnull); static Datum ExecJustAssignScanVarVirt(ExprState *state, ExprContext *econtext, bool *isnull); +/* execution helper functions */ +static pg_attribute_always_inline void +ExecAggPlainTransByVal(AggState *aggstate, AggStatePerTrans pertrans, + AggStatePerGroup pergroup, + ExprContext *aggcontext, int setno); + +static pg_attribute_always_inline void +ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans, + AggStatePerGroup pergroup, + ExprContext *aggcontext, int setno); /* * Prepare ExprState for interpreted execution. @@ -425,10 +435,12 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) &&CASE_EEOP_AGG_DESERIALIZE, &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_ARGS, &&CASE_EEOP_AGG_STRICT_INPUT_CHECK_NULLS, - &&CASE_EEOP_AGG_INIT_TRANS, - &&CASE_EEOP_AGG_STRICT_TRANS_CHECK, + &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL, + &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL, &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL, - &&CASE_EEOP_AGG_PLAIN_TRANS, + &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF, + &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYREF, + &&CASE_EEOP_AGG_PLAIN_TRANS_BYREF, &&CASE_EEOP_AGG_ORDERED_TRANS_DATUM, &&CASE_EEOP_AGG_ORDERED_TRANS_TUPLE, &&CASE_EEOP_LAST @@ -1592,167 +1604,136 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull) } /* - * Initialize an aggregate's first value if necessary. + * Different types of aggregate transition functions are implemented + * as different types of steps, to avoid incurring unnecessary + * overhead. There's a step type for each valid combination of having + * a by value / by reference transition type, [not] needing to the + * initialize the transition value for the first row in a group from + * input, and [not] strict transition function. + * + * Could optimize further by splitting off by-reference for + * fixed-length types, but currently that doesn't seem worth it. */ - EEO_CASE(EEOP_AGG_INIT_TRANS) + + EEO_CASE(EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL) { AggState *aggstate = castNode(AggState, state->parent); - AggStatePerGroup pergroup; - - pergroup = &aggstate->all_pergroups - [op->d.agg_init_trans.setoff] - [op->d.agg_init_trans.transno]; - - /* If transValue has not yet been initialized, do so now. */ - if (pergroup->noTransValue) - { - AggStatePerTrans pertrans = op->d.agg_init_trans.pertrans; - - aggstate->curaggcontext = op->d.agg_init_trans.aggcontext; - aggstate->current_set = op->d.agg_init_trans.setno; - - ExecAggInitGroup(aggstate, pertrans, pergroup); - - /* copied trans value from input, done this round */ - EEO_JUMP(op->d.agg_init_trans.jumpnull); - } - - EEO_NEXT(); - } - - /* check that a strict aggregate's input isn't NULL */ - EEO_CASE(EEOP_AGG_STRICT_TRANS_CHECK) - { - AggState *aggstate = castNode(AggState, state->parent); - AggStatePerGroup pergroup; - - pergroup = &aggstate->all_pergroups - [op->d.agg_strict_trans_check.setoff] - [op->d.agg_strict_trans_check.transno]; - - if (unlikely(pergroup->transValueIsNull)) - EEO_JUMP(op->d.agg_strict_trans_check.jumpnull); - - EEO_NEXT(); - } - - /* - * Evaluate aggregate transition / combine function that has a - * by-value transition type. That's a separate case from the - * by-reference implementation because it's a bit simpler. - */ - EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL) - { - AggState *aggstate = castNode(AggState, state->parent); - AggStatePerTrans pertrans; - AggStatePerGroup pergroup; - FunctionCallInfo fcinfo; - MemoryContext oldContext; - Datum newVal; - - pertrans = op->d.agg_trans.pertrans; - - pergroup = &aggstate->all_pergroups + AggStatePerTrans pertrans = op->d.agg_trans.pertrans; + AggStatePerGroup pergroup = &aggstate->all_pergroups [op->d.agg_trans.setoff] [op->d.agg_trans.transno]; Assert(pertrans->transtypeByVal); - fcinfo = pertrans->transfn_fcinfo; - - /* cf. select_current_set() */ - aggstate->curaggcontext = op->d.agg_trans.aggcontext; - aggstate->current_set = op->d.agg_trans.setno; - - /* set up aggstate->curpertrans for AggGetAggref() */ - aggstate->curpertrans = pertrans; - - /* invoke transition function in per-tuple context */ - oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); - - fcinfo->args[0].value = pergroup->transValue; - fcinfo->args[0].isnull = pergroup->transValueIsNull; - fcinfo->isnull = false; /* just in case transfn doesn't set it */ - - newVal = FunctionCallInvoke(fcinfo); - - pergroup->transValue = newVal; - pergroup->transValueIsNull = fcinfo->isnull; - - MemoryContextSwitchTo(oldContext); + if (pergroup->noTransValue) + { + /* If transValue has not yet been initialized, do so now. */ + ExecAggInitGroup(aggstate, pertrans, pergroup, + op->d.agg_trans.aggcontext); + /* copied trans value from input, done this round */ + } + else if (likely(!pergroup->transValueIsNull)) + { + /* invoke transition function, unless prevented by strictness */ + ExecAggPlainTransByVal(aggstate, pertrans, pergroup, + op->d.agg_trans.aggcontext, + op->d.agg_trans.setno); + } EEO_NEXT(); } - /* - * Evaluate aggregate transition / combine function that has a - * by-reference transition type. - * - * Could optimize a bit further by splitting off by-reference - * fixed-length types, but currently that doesn't seem worth it. - */ - EEO_CASE(EEOP_AGG_PLAIN_TRANS) + /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ + EEO_CASE(EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL) { AggState *aggstate = castNode(AggState, state->parent); - AggStatePerTrans pertrans; - AggStatePerGroup pergroup; - FunctionCallInfo fcinfo; - MemoryContext oldContext; - Datum newVal; + AggStatePerTrans pertrans = op->d.agg_trans.pertrans; + AggStatePerGroup pergroup = &aggstate->all_pergroups + [op->d.agg_trans.setoff] + [op->d.agg_trans.transno]; - pertrans = op->d.agg_trans.pertrans; + Assert(pertrans->transtypeByVal); - pergroup = &aggstate->all_pergroups + if (likely(!pergroup->transValueIsNull)) + ExecAggPlainTransByVal(aggstate, pertrans, pergroup, + op->d.agg_trans.aggcontext, + op->d.agg_trans.setno); + + EEO_NEXT(); + } + + /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ + EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYVAL) + { + AggState *aggstate = castNode(AggState, state->parent); + AggStatePerTrans pertrans = op->d.agg_trans.pertrans; + AggStatePerGroup pergroup = &aggstate->all_pergroups + [op->d.agg_trans.setoff] + [op->d.agg_trans.transno]; + + Assert(pertrans->transtypeByVal); + + ExecAggPlainTransByVal(aggstate, pertrans, pergroup, + op->d.agg_trans.aggcontext, + op->d.agg_trans.setno); + + EEO_NEXT(); + } + + /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ + EEO_CASE(EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF) + { + AggState *aggstate = castNode(AggState, state->parent); + AggStatePerTrans pertrans = op->d.agg_trans.pertrans; + AggStatePerGroup pergroup = &aggstate->all_pergroups [op->d.agg_trans.setoff] [op->d.agg_trans.transno]; Assert(!pertrans->transtypeByVal); - fcinfo = pertrans->transfn_fcinfo; + if (pergroup->noTransValue) + ExecAggInitGroup(aggstate, pertrans, pergroup, + op->d.agg_trans.aggcontext); + else if (likely(!pergroup->transValueIsNull)) + ExecAggPlainTransByRef(aggstate, pertrans, pergroup, + op->d.agg_trans.aggcontext, + op->d.agg_trans.setno); - /* cf. select_current_set() */ - aggstate->curaggcontext = op->d.agg_trans.aggcontext; - aggstate->current_set = op->d.agg_trans.setno; + EEO_NEXT(); + } - /* set up aggstate->curpertrans for AggGetAggref() */ - aggstate->curpertrans = pertrans; + /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ + EEO_CASE(EEOP_AGG_PLAIN_TRANS_STRICT_BYREF) + { + AggState *aggstate = castNode(AggState, state->parent); + AggStatePerTrans pertrans = op->d.agg_trans.pertrans; + AggStatePerGroup pergroup = &aggstate->all_pergroups + [op->d.agg_trans.setoff] + [op->d.agg_trans.transno]; - /* invoke transition function in per-tuple context */ - oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); + Assert(!pertrans->transtypeByVal); - fcinfo->args[0].value = pergroup->transValue; - fcinfo->args[0].isnull = pergroup->transValueIsNull; - fcinfo->isnull = false; /* just in case transfn doesn't set it */ + if (likely(!pergroup->transValueIsNull)) + ExecAggPlainTransByRef(aggstate, pertrans, pergroup, + op->d.agg_trans.aggcontext, + op->d.agg_trans.setno); + EEO_NEXT(); + } - newVal = FunctionCallInvoke(fcinfo); + /* see comments above EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL */ + EEO_CASE(EEOP_AGG_PLAIN_TRANS_BYREF) + { + AggState *aggstate = castNode(AggState, state->parent); + AggStatePerTrans pertrans = op->d.agg_trans.pertrans; + AggStatePerGroup pergroup = &aggstate->all_pergroups + [op->d.agg_trans.setoff] + [op->d.agg_trans.transno]; - /* - * For pass-by-ref datatype, must copy the new value into - * aggcontext and free the prior transValue. But if transfn - * returned a pointer to its first input, we don't need to do - * anything. Also, if transfn returned a pointer to a R/W - * expanded object that is already a child of the aggcontext, - * assume we can adopt that value without copying it. - * - * It's safe to compare newVal with pergroup->transValue without - * regard for either being NULL, because ExecAggTransReparent() - * takes care to set transValue to 0 when NULL. Otherwise we could - * end up accidentally not reparenting, when the transValue has - * the same numerical value as newValue, despite being NULL. This - * is a somewhat hot path, making it undesirable to instead solve - * this with another branch for the common case of the transition - * function returning its (modified) input argument. - */ - if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue)) - newVal = ExecAggTransReparent(aggstate, pertrans, - newVal, fcinfo->isnull, - pergroup->transValue, - pergroup->transValueIsNull); + Assert(!pertrans->transtypeByVal); - pergroup->transValue = newVal; - pergroup->transValueIsNull = fcinfo->isnull; - - MemoryContextSwitchTo(oldContext); + ExecAggPlainTransByRef(aggstate, pertrans, pergroup, + op->d.agg_trans.aggcontext, + op->d.agg_trans.setno); EEO_NEXT(); } @@ -4145,7 +4126,8 @@ ExecEvalSysVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext, * value for a group. We use it as the initial value for transValue. */ void -ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup) +ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup, + ExprContext *aggcontext) { FunctionCallInfo fcinfo = pertrans->transfn_fcinfo; MemoryContext oldContext; @@ -4156,7 +4138,7 @@ ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup * that the agg's input type is binary-compatible with its transtype, so * straight copy here is OK.) */ - oldContext = MemoryContextSwitchTo(aggstate->curaggcontext->ecxt_per_tuple_memory); + oldContext = MemoryContextSwitchTo(aggcontext->ecxt_per_tuple_memory); pergroup->transValue = datumCopy(fcinfo->args[1].value, pertrans->transtypeByVal, pertrans->transtypeLen); @@ -4243,3 +4225,90 @@ ExecEvalAggOrderedTransTuple(ExprState *state, ExprEvalStep *op, ExecStoreVirtualTuple(pertrans->sortslot); tuplesort_puttupleslot(pertrans->sortstates[setno], pertrans->sortslot); } + +/* implementation of transition function invocation for byval types */ +static pg_attribute_always_inline void +ExecAggPlainTransByVal(AggState *aggstate, AggStatePerTrans pertrans, + AggStatePerGroup pergroup, + ExprContext *aggcontext, int setno) +{ + FunctionCallInfo fcinfo = pertrans->transfn_fcinfo; + MemoryContext oldContext; + Datum newVal; + + /* cf. select_current_set() */ + aggstate->curaggcontext = aggcontext; + aggstate->current_set = setno; + + /* set up aggstate->curpertrans for AggGetAggref() */ + aggstate->curpertrans = pertrans; + + /* invoke transition function in per-tuple context */ + oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); + + fcinfo->args[0].value = pergroup->transValue; + fcinfo->args[0].isnull = pergroup->transValueIsNull; + fcinfo->isnull = false; /* just in case transfn doesn't set it */ + + newVal = FunctionCallInvoke(fcinfo); + + pergroup->transValue = newVal; + pergroup->transValueIsNull = fcinfo->isnull; + + MemoryContextSwitchTo(oldContext); +} + +/* implementation of transition function invocation for byref types */ +static pg_attribute_always_inline void +ExecAggPlainTransByRef(AggState *aggstate, AggStatePerTrans pertrans, + AggStatePerGroup pergroup, + ExprContext *aggcontext, int setno) +{ + FunctionCallInfo fcinfo = pertrans->transfn_fcinfo; + MemoryContext oldContext; + Datum newVal; + + /* cf. select_current_set() */ + aggstate->curaggcontext = aggcontext; + aggstate->current_set = setno; + + /* set up aggstate->curpertrans for AggGetAggref() */ + aggstate->curpertrans = pertrans; + + /* invoke transition function in per-tuple context */ + oldContext = MemoryContextSwitchTo(aggstate->tmpcontext->ecxt_per_tuple_memory); + + fcinfo->args[0].value = pergroup->transValue; + fcinfo->args[0].isnull = pergroup->transValueIsNull; + fcinfo->isnull = false; /* just in case transfn doesn't set it */ + + newVal = FunctionCallInvoke(fcinfo); + + /* + * For pass-by-ref datatype, must copy the new value into + * aggcontext and free the prior transValue. But if transfn + * returned a pointer to its first input, we don't need to do + * anything. Also, if transfn returned a pointer to a R/W + * expanded object that is already a child of the aggcontext, + * assume we can adopt that value without copying it. + * + * It's safe to compare newVal with pergroup->transValue without + * regard for either being NULL, because ExecAggTransReparent() + * takes care to set transValue to 0 when NULL. Otherwise we could + * end up accidentally not reparenting, when the transValue has + * the same numerical value as newValue, despite being NULL. This + * is a somewhat hot path, making it undesirable to instead solve + * this with another branch for the common case of the transition + * function returning its (modified) input argument. + */ + if (DatumGetPointer(newVal) != DatumGetPointer(pergroup->transValue)) + newVal = ExecAggTransReparent(aggstate, pertrans, + newVal, fcinfo->isnull, + pergroup->transValue, + pergroup->transValueIsNull); + + pergroup->transValue = newVal; + pergroup->transValueIsNull = fcinfo->isnull; + + MemoryContextSwitchTo(oldContext); +} diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index a99b4a60754..13c21ffe9a3 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -304,7 +304,10 @@ static int find_compatible_pertrans(AggState *aggstate, Aggref *newagg, static void select_current_set(AggState *aggstate, int setno, bool is_hash) { - /* when changing this, also adapt ExecInterpExpr() and friends */ + /* + * When changing this, also adapt ExecAggPlainTransByVal() and + * ExecAggPlainTransByRef(). + */ if (is_hash) aggstate->curaggcontext = aggstate->hashcontext; else diff --git a/src/backend/jit/llvm/llvmjit_expr.c b/src/backend/jit/llvm/llvmjit_expr.c index cea0d6fa5ce..dc16b399327 100644 --- a/src/backend/jit/llvm/llvmjit_expr.c +++ b/src/backend/jit/llvm/llvmjit_expr.c @@ -2046,152 +2046,12 @@ llvm_compile_expr(ExprState *state) break; } - case EEOP_AGG_INIT_TRANS: - { - AggStatePerTrans pertrans; - - LLVMValueRef v_aggstatep; - LLVMValueRef v_pertransp; - - LLVMValueRef v_allpergroupsp; - - LLVMValueRef v_pergroupp; - - LLVMValueRef v_setoff, - v_transno; - - LLVMValueRef v_notransvalue; - - LLVMBasicBlockRef b_init; - - pertrans = op->d.agg_init_trans.pertrans; - - v_aggstatep = - LLVMBuildBitCast(b, v_parent, l_ptr(StructAggState), ""); - v_pertransp = l_ptr_const(pertrans, - l_ptr(StructAggStatePerTransData)); - - /* - * pergroup = &aggstate->all_pergroups - * [op->d.agg_init_trans_check.setoff] - * [op->d.agg_init_trans_check.transno]; - */ - v_allpergroupsp = - l_load_struct_gep(b, v_aggstatep, - FIELDNO_AGGSTATE_ALL_PERGROUPS, - "aggstate.all_pergroups"); - v_setoff = l_int32_const(op->d.agg_init_trans.setoff); - v_transno = l_int32_const(op->d.agg_init_trans.transno); - v_pergroupp = - LLVMBuildGEP(b, - l_load_gep1(b, v_allpergroupsp, v_setoff, ""), - &v_transno, 1, ""); - - v_notransvalue = - l_load_struct_gep(b, v_pergroupp, - FIELDNO_AGGSTATEPERGROUPDATA_NOTRANSVALUE, - "notransvalue"); - - b_init = l_bb_before_v(opblocks[opno + 1], - "op.%d.inittrans", opno); - - LLVMBuildCondBr(b, - LLVMBuildICmp(b, LLVMIntEQ, v_notransvalue, - l_sbool_const(1), ""), - b_init, - opblocks[opno + 1]); - - LLVMPositionBuilderAtEnd(b, b_init); - - { - LLVMValueRef params[3]; - LLVMValueRef v_curaggcontext; - LLVMValueRef v_current_set; - LLVMValueRef v_aggcontext; - - v_aggcontext = l_ptr_const(op->d.agg_init_trans.aggcontext, - l_ptr(StructExprContext)); - - v_current_set = - LLVMBuildStructGEP(b, - v_aggstatep, - FIELDNO_AGGSTATE_CURRENT_SET, - "aggstate.current_set"); - v_curaggcontext = - LLVMBuildStructGEP(b, - v_aggstatep, - FIELDNO_AGGSTATE_CURAGGCONTEXT, - "aggstate.curaggcontext"); - - LLVMBuildStore(b, l_int32_const(op->d.agg_init_trans.setno), - v_current_set); - LLVMBuildStore(b, v_aggcontext, - v_curaggcontext); - - params[0] = v_aggstatep; - params[1] = v_pertransp; - params[2] = v_pergroupp; - - LLVMBuildCall(b, - llvm_pg_func(mod, "ExecAggInitGroup"), - params, lengthof(params), - ""); - } - LLVMBuildBr(b, opblocks[op->d.agg_init_trans.jumpnull]); - - break; - } - - case EEOP_AGG_STRICT_TRANS_CHECK: - { - LLVMValueRef v_setoff, - v_transno; - - LLVMValueRef v_aggstatep; - LLVMValueRef v_allpergroupsp; - - LLVMValueRef v_transnull; - LLVMValueRef v_pergroupp; - - int jumpnull = op->d.agg_strict_trans_check.jumpnull; - - v_aggstatep = - LLVMBuildBitCast(b, v_parent, l_ptr(StructAggState), ""); - - /* - * pergroup = &aggstate->all_pergroups - * [op->d.agg_strict_trans_check.setoff] - * [op->d.agg_init_trans_check.transno]; - */ - v_allpergroupsp = - l_load_struct_gep(b, v_aggstatep, - FIELDNO_AGGSTATE_ALL_PERGROUPS, - "aggstate.all_pergroups"); - v_setoff = - l_int32_const(op->d.agg_strict_trans_check.setoff); - v_transno = - l_int32_const(op->d.agg_strict_trans_check.transno); - v_pergroupp = - LLVMBuildGEP(b, - l_load_gep1(b, v_allpergroupsp, v_setoff, ""), - &v_transno, 1, ""); - - v_transnull = - l_load_struct_gep(b, v_pergroupp, - FIELDNO_AGGSTATEPERGROUPDATA_TRANSVALUEISNULL, - "transnull"); - - LLVMBuildCondBr(b, - LLVMBuildICmp(b, LLVMIntEQ, v_transnull, - l_sbool_const(1), ""), - opblocks[jumpnull], - opblocks[opno + 1]); - - break; - } - + case EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL: + case EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL: case EEOP_AGG_PLAIN_TRANS_BYVAL: - case EEOP_AGG_PLAIN_TRANS: + case EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF: + case EEOP_AGG_PLAIN_TRANS_STRICT_BYREF: + case EEOP_AGG_PLAIN_TRANS_BYREF: { AggState *aggstate; AggStatePerTrans pertrans; @@ -2249,6 +2109,81 @@ llvm_compile_expr(ExprState *state) l_load_gep1(b, v_allpergroupsp, v_setoff, ""), &v_transno, 1, ""); + + if (opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL || + opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF) + { + LLVMValueRef v_notransvalue; + LLVMBasicBlockRef b_init; + LLVMBasicBlockRef b_no_init; + + v_notransvalue = + l_load_struct_gep(b, v_pergroupp, + FIELDNO_AGGSTATEPERGROUPDATA_NOTRANSVALUE, + "notransvalue"); + + b_init = l_bb_before_v(opblocks[opno + 1], + "op.%d.inittrans", opno); + b_no_init = l_bb_before_v(opblocks[opno + 1], + "op.%d.no_inittrans", opno); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_notransvalue, + l_sbool_const(1), ""), + b_init, + b_no_init); + + /* block to init the transition value if necessary */ + { + LLVMValueRef params[4]; + + LLVMPositionBuilderAtEnd(b, b_init); + + v_aggcontext = l_ptr_const(op->d.agg_trans.aggcontext, + l_ptr(StructExprContext)); + + params[0] = v_aggstatep; + params[1] = v_pertransp; + params[2] = v_pergroupp; + params[3] = v_aggcontext; + + LLVMBuildCall(b, + llvm_pg_func(mod, "ExecAggInitGroup"), + params, lengthof(params), + ""); + + LLVMBuildBr(b, opblocks[opno + 1]); + + } + + LLVMPositionBuilderAtEnd(b, b_no_init); + } + + if (opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL || + opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF || + opcode == EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL || + opcode == EEOP_AGG_PLAIN_TRANS_STRICT_BYREF) + { + LLVMValueRef v_transnull; + LLVMBasicBlockRef b_strictpass; + + b_strictpass = l_bb_before_v(opblocks[opno + 1], + "op.%d.strictpass", opno); + v_transnull = + l_load_struct_gep(b, v_pergroupp, + FIELDNO_AGGSTATEPERGROUPDATA_TRANSVALUEISNULL, + "transnull"); + + LLVMBuildCondBr(b, + LLVMBuildICmp(b, LLVMIntEQ, v_transnull, + l_sbool_const(1), ""), + opblocks[opno + 1], + b_strictpass); + + LLVMPositionBuilderAtEnd(b, b_strictpass); + } + + v_fcinfo = l_ptr_const(fcinfo, l_ptr(StructFunctionCallInfoData)); v_aggcontext = l_ptr_const(op->d.agg_trans.aggcontext, @@ -2312,7 +2247,9 @@ llvm_compile_expr(ExprState *state) * child of the aggcontext, assume we can adopt that value * without copying it. */ - if (opcode == EEOP_AGG_PLAIN_TRANS) + if (opcode == EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF || + opcode == EEOP_AGG_PLAIN_TRANS_STRICT_BYREF || + opcode == EEOP_AGG_PLAIN_TRANS_BYREF) { LLVMBasicBlockRef b_call; LLVMBasicBlockRef b_nocall; diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h index 73a2ca8c6dd..8bbf6621da0 100644 --- a/src/include/executor/execExpr.h +++ b/src/include/executor/execExpr.h @@ -225,10 +225,12 @@ typedef enum ExprEvalOp EEOP_AGG_DESERIALIZE, EEOP_AGG_STRICT_INPUT_CHECK_ARGS, EEOP_AGG_STRICT_INPUT_CHECK_NULLS, - EEOP_AGG_INIT_TRANS, - EEOP_AGG_STRICT_TRANS_CHECK, + EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL, + EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL, EEOP_AGG_PLAIN_TRANS_BYVAL, - EEOP_AGG_PLAIN_TRANS, + EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYREF, + EEOP_AGG_PLAIN_TRANS_STRICT_BYREF, + EEOP_AGG_PLAIN_TRANS_BYREF, EEOP_AGG_ORDERED_TRANS_DATUM, EEOP_AGG_ORDERED_TRANS_TUPLE, @@ -620,27 +622,8 @@ typedef struct ExprEvalStep int jumpnull; } agg_strict_input_check; - /* for EEOP_AGG_INIT_TRANS */ - struct - { - AggStatePerTrans pertrans; - ExprContext *aggcontext; - int setno; - int transno; - int setoff; - int jumpnull; - } agg_init_trans; - - /* for EEOP_AGG_STRICT_TRANS_CHECK */ - struct - { - int setno; - int transno; - int setoff; - int jumpnull; - } agg_strict_trans_check; - - /* for EEOP_AGG_{PLAIN,ORDERED}_TRANS* */ + /* for EEOP_AGG_PLAIN_TRANS_[INIT_][STRICT_]{BYVAL,BYREF} */ + /* for EEOP_AGG_ORDERED_TRANS_{DATUM,TUPLE} */ struct { AggStatePerTrans pertrans; @@ -750,7 +733,8 @@ extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, extern void ExecEvalSysVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext, TupleTableSlot *slot); -extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup); +extern void ExecAggInitGroup(AggState *aggstate, AggStatePerTrans pertrans, AggStatePerGroup pergroup, + ExprContext *aggcontext); extern Datum ExecAggTransReparent(AggState *aggstate, AggStatePerTrans pertrans, Datum newValue, bool newValueIsNull, Datum oldValue, bool oldValueIsNull);