| 1 | /*------------------------------------------------------------------------- |
| 2 | * |
| 3 | * nodeGroup.c |
| 4 | * Routines to handle group nodes (used for queries with GROUP BY clause). |
| 5 | * |
| 6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
| 7 | * Portions Copyright (c) 1994, Regents of the University of California |
| 8 | * |
| 9 | * |
| 10 | * DESCRIPTION |
| 11 | * The Group node is designed for handling queries with a GROUP BY clause. |
| 12 | * Its outer plan must deliver tuples that are sorted in the order |
| 13 | * specified by the grouping columns (ie. tuples from the same group are |
| 14 | * consecutive). That way, we just have to compare adjacent tuples to |
| 15 | * locate group boundaries. |
| 16 | * |
| 17 | * IDENTIFICATION |
| 18 | * src/backend/executor/nodeGroup.c |
| 19 | * |
| 20 | *------------------------------------------------------------------------- |
| 21 | */ |
| 22 | |
| 23 | #include "postgres.h" |
| 24 | |
| 25 | #include "executor/executor.h" |
| 26 | #include "executor/nodeGroup.h" |
| 27 | #include "miscadmin.h" |
| 28 | #include "utils/memutils.h" |
| 29 | |
| 30 | |
| 31 | /* |
| 32 | * ExecGroup - |
| 33 | * |
| 34 | * Return one tuple for each group of matching input tuples. |
| 35 | */ |
| 36 | static TupleTableSlot * |
| 37 | ExecGroup(PlanState *pstate) |
| 38 | { |
| 39 | GroupState *node = castNode(GroupState, pstate); |
| 40 | ExprContext *econtext; |
| 41 | TupleTableSlot *firsttupleslot; |
| 42 | TupleTableSlot *outerslot; |
| 43 | |
| 44 | CHECK_FOR_INTERRUPTS(); |
| 45 | |
| 46 | /* |
| 47 | * get state info from node |
| 48 | */ |
| 49 | if (node->grp_done) |
| 50 | return NULL; |
| 51 | econtext = node->ss.ps.ps_ExprContext; |
| 52 | |
| 53 | /* |
| 54 | * The ScanTupleSlot holds the (copied) first tuple of each group. |
| 55 | */ |
| 56 | firsttupleslot = node->ss.ss_ScanTupleSlot; |
| 57 | |
| 58 | /* |
| 59 | * We need not call ResetExprContext here because ExecQualAndReset() will |
| 60 | * reset the per-tuple memory context once per input tuple. |
| 61 | */ |
| 62 | |
| 63 | /* |
| 64 | * If first time through, acquire first input tuple and determine whether |
| 65 | * to return it or not. |
| 66 | */ |
| 67 | if (TupIsNull(firsttupleslot)) |
| 68 | { |
| 69 | outerslot = ExecProcNode(outerPlanState(node)); |
| 70 | if (TupIsNull(outerslot)) |
| 71 | { |
| 72 | /* empty input, so return nothing */ |
| 73 | node->grp_done = true; |
| 74 | return NULL; |
| 75 | } |
| 76 | /* Copy tuple into firsttupleslot */ |
| 77 | ExecCopySlot(firsttupleslot, outerslot); |
| 78 | |
| 79 | /* |
| 80 | * Set it up as input for qual test and projection. The expressions |
| 81 | * will access the input tuple as varno OUTER. |
| 82 | */ |
| 83 | econtext->ecxt_outertuple = firsttupleslot; |
| 84 | |
| 85 | /* |
| 86 | * Check the qual (HAVING clause); if the group does not match, ignore |
| 87 | * it and fall into scan loop. |
| 88 | */ |
| 89 | if (ExecQual(node->ss.ps.qual, econtext)) |
| 90 | { |
| 91 | /* |
| 92 | * Form and return a projection tuple using the first input tuple. |
| 93 | */ |
| 94 | return ExecProject(node->ss.ps.ps_ProjInfo); |
| 95 | } |
| 96 | else |
| 97 | InstrCountFiltered1(node, 1); |
| 98 | } |
| 99 | |
| 100 | /* |
| 101 | * This loop iterates once per input tuple group. At the head of the |
| 102 | * loop, we have finished processing the first tuple of the group and now |
| 103 | * need to scan over all the other group members. |
| 104 | */ |
| 105 | for (;;) |
| 106 | { |
| 107 | /* |
| 108 | * Scan over all remaining tuples that belong to this group |
| 109 | */ |
| 110 | for (;;) |
| 111 | { |
| 112 | outerslot = ExecProcNode(outerPlanState(node)); |
| 113 | if (TupIsNull(outerslot)) |
| 114 | { |
| 115 | /* no more groups, so we're done */ |
| 116 | node->grp_done = true; |
| 117 | return NULL; |
| 118 | } |
| 119 | |
| 120 | /* |
| 121 | * Compare with first tuple and see if this tuple is of the same |
| 122 | * group. If so, ignore it and keep scanning. |
| 123 | */ |
| 124 | econtext->ecxt_innertuple = firsttupleslot; |
| 125 | econtext->ecxt_outertuple = outerslot; |
| 126 | if (!ExecQualAndReset(node->eqfunction, econtext)) |
| 127 | break; |
| 128 | } |
| 129 | |
| 130 | /* |
| 131 | * We have the first tuple of the next input group. See if we want to |
| 132 | * return it. |
| 133 | */ |
| 134 | /* Copy tuple, set up as input for qual test and projection */ |
| 135 | ExecCopySlot(firsttupleslot, outerslot); |
| 136 | econtext->ecxt_outertuple = firsttupleslot; |
| 137 | |
| 138 | /* |
| 139 | * Check the qual (HAVING clause); if the group does not match, ignore |
| 140 | * it and loop back to scan the rest of the group. |
| 141 | */ |
| 142 | if (ExecQual(node->ss.ps.qual, econtext)) |
| 143 | { |
| 144 | /* |
| 145 | * Form and return a projection tuple using the first input tuple. |
| 146 | */ |
| 147 | return ExecProject(node->ss.ps.ps_ProjInfo); |
| 148 | } |
| 149 | else |
| 150 | InstrCountFiltered1(node, 1); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | /* ----------------- |
| 155 | * ExecInitGroup |
| 156 | * |
| 157 | * Creates the run-time information for the group node produced by the |
| 158 | * planner and initializes its outer subtree |
| 159 | * ----------------- |
| 160 | */ |
| 161 | GroupState * |
| 162 | ExecInitGroup(Group *node, EState *estate, int eflags) |
| 163 | { |
| 164 | GroupState *grpstate; |
| 165 | const TupleTableSlotOps *tts_ops; |
| 166 | |
| 167 | /* check for unsupported flags */ |
| 168 | Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); |
| 169 | |
| 170 | /* |
| 171 | * create state structure |
| 172 | */ |
| 173 | grpstate = makeNode(GroupState); |
| 174 | grpstate->ss.ps.plan = (Plan *) node; |
| 175 | grpstate->ss.ps.state = estate; |
| 176 | grpstate->ss.ps.ExecProcNode = ExecGroup; |
| 177 | grpstate->grp_done = false; |
| 178 | |
| 179 | /* |
| 180 | * create expression context |
| 181 | */ |
| 182 | ExecAssignExprContext(estate, &grpstate->ss.ps); |
| 183 | |
| 184 | /* |
| 185 | * initialize child nodes |
| 186 | */ |
| 187 | outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags); |
| 188 | |
| 189 | /* |
| 190 | * Initialize scan slot and type. |
| 191 | */ |
| 192 | tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL); |
| 193 | ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops); |
| 194 | |
| 195 | /* |
| 196 | * Initialize result slot, type and projection. |
| 197 | */ |
| 198 | ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual); |
| 199 | ExecAssignProjectionInfo(&grpstate->ss.ps, NULL); |
| 200 | |
| 201 | /* |
| 202 | * initialize child expressions |
| 203 | */ |
| 204 | grpstate->ss.ps.qual = |
| 205 | ExecInitQual(node->plan.qual, (PlanState *) grpstate); |
| 206 | |
| 207 | /* |
| 208 | * Precompute fmgr lookup data for inner loop |
| 209 | */ |
| 210 | grpstate->eqfunction = |
| 211 | execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)), |
| 212 | node->numCols, |
| 213 | node->grpColIdx, |
| 214 | node->grpOperators, |
| 215 | node->grpCollations, |
| 216 | &grpstate->ss.ps); |
| 217 | |
| 218 | return grpstate; |
| 219 | } |
| 220 | |
| 221 | /* ------------------------ |
| 222 | * ExecEndGroup(node) |
| 223 | * |
| 224 | * ----------------------- |
| 225 | */ |
| 226 | void |
| 227 | ExecEndGroup(GroupState *node) |
| 228 | { |
| 229 | PlanState *outerPlan; |
| 230 | |
| 231 | ExecFreeExprContext(&node->ss.ps); |
| 232 | |
| 233 | /* clean up tuple table */ |
| 234 | ExecClearTuple(node->ss.ss_ScanTupleSlot); |
| 235 | |
| 236 | outerPlan = outerPlanState(node); |
| 237 | ExecEndNode(outerPlan); |
| 238 | } |
| 239 | |
| 240 | void |
| 241 | ExecReScanGroup(GroupState *node) |
| 242 | { |
| 243 | PlanState *outerPlan = outerPlanState(node); |
| 244 | |
| 245 | node->grp_done = false; |
| 246 | /* must clear first tuple */ |
| 247 | ExecClearTuple(node->ss.ss_ScanTupleSlot); |
| 248 | |
| 249 | /* |
| 250 | * if chgParam of subnode is not null then plan will be re-scanned by |
| 251 | * first ExecProcNode. |
| 252 | */ |
| 253 | if (outerPlan->chgParam == NULL) |
| 254 | ExecReScan(outerPlan); |
| 255 | } |
| 256 | |