Extend ExecBuildAggTrans() to support a NULL pointer check.
authorJeff Davis <jdavis@postgresql.org>
Thu, 5 Mar 2020 01:20:20 +0000 (17:20 -0800)
committerJeff Davis <jdavis@postgresql.org>
Thu, 5 Mar 2020 01:29:18 +0000 (17:29 -0800)
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

src/backend/executor/execExpr.c
src/backend/executor/execExprInterp.c
src/backend/executor/nodeAgg.c
src/backend/jit/llvm/llvmjit_expr.c
src/include/executor/execExpr.h
src/include/executor/executor.h

index 91aa386fa61e7cfc84810bc80eb27698aad5f783..8c5ead93d680baef80ecc0fdbffd7d78a99a4f03 100644 (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;
+   }
 }
 
 /*
index eafd484900206637f47171791d3c52bdb53d3022..113ed1547cbc941010b6fe0b2429ecc79d963f38 100644 (file)
@@ -435,6 +435,7 @@ 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_PLAIN_PERGROUP_NULLCHECK,
        &&CASE_EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
        &&CASE_EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
        &&CASE_EEOP_AGG_PLAIN_TRANS_BYVAL,
@@ -1603,6 +1604,22 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
            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
         * as different types of steps, to avoid incurring unnecessary
index 13c21ffe9a37af127c0061527548959cdcf6e4c1..7aebb247d8895ed56acef5d9f0a71d1ca2f19d3e 100644 (file)
@@ -2928,7 +2928,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
        else
            Assert(false);
 
-       phase->evaltrans = ExecBuildAggTrans(aggstate, phase, dosort, dohash);
+       phase->evaltrans = ExecBuildAggTrans(aggstate, phase, dosort, dohash,
+                                            false);
 
    }
 
index dc16b3993275b316bae83612370a9b7d4d10623a..b855e739571097d797c42e18be3bdc964a1b3fdd 100644 (file)
@@ -2046,6 +2046,45 @@ llvm_compile_expr(ExprState *state)
                    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_STRICT_BYVAL:
            case EEOP_AGG_PLAIN_TRANS_BYVAL:
index 8bbf6621da0e36d933e699e605b265c1dc229ed9..dbe8649a5763f23ef9bbc1493fbe1a5775c39dd2 100644 (file)
@@ -225,6 +225,7 @@ typedef enum ExprEvalOp
    EEOP_AGG_DESERIALIZE,
    EEOP_AGG_STRICT_INPUT_CHECK_ARGS,
    EEOP_AGG_STRICT_INPUT_CHECK_NULLS,
+   EEOP_AGG_PLAIN_PERGROUP_NULLCHECK,
    EEOP_AGG_PLAIN_TRANS_INIT_STRICT_BYVAL,
    EEOP_AGG_PLAIN_TRANS_STRICT_BYVAL,
    EEOP_AGG_PLAIN_TRANS_BYVAL,
@@ -622,6 +623,13 @@ typedef struct ExprEvalStep
            int         jumpnull;
        }           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_ORDERED_TRANS_{DATUM,TUPLE} */
        struct
index 81fdfa4add32164660ae09136e63969050234e33..94890512dc881806aa31c4c2dc9670a3d46314bd 100644 (file)
@@ -255,7 +255,7 @@ extern ExprState *ExecInitQual(List *qual, PlanState *parent);
 extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
 extern List *ExecInitExprList(List *nodes, PlanState *parent);
 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,
                                         const TupleTableSlotOps *lops, const TupleTableSlotOps *rops,
                                         int numCols,