Line data Source code
1 : /*------------------------------------------------------------------------- 2 : * 3 : * nodeGroup.c 4 : * Routines to handle group nodes (used for queries with GROUP BY clause). 5 : * 6 : * Portions Copyright (c) 1996-2023, 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 7958 : ExecGroup(PlanState *pstate) 38 : { 39 7958 : GroupState *node = castNode(GroupState, pstate); 40 : ExprContext *econtext; 41 : TupleTableSlot *firsttupleslot; 42 : TupleTableSlot *outerslot; 43 : 44 7958 : CHECK_FOR_INTERRUPTS(); 45 : 46 : /* 47 : * get state info from node 48 : */ 49 7958 : if (node->grp_done) 50 0 : return NULL; 51 7958 : econtext = node->ss.ps.ps_ExprContext; 52 : 53 : /* 54 : * The ScanTupleSlot holds the (copied) first tuple of each group. 55 : */ 56 7958 : 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 7958 : if (TupIsNull(firsttupleslot)) 68 : { 69 160 : outerslot = ExecProcNode(outerPlanState(node)); 70 160 : if (TupIsNull(outerslot)) 71 : { 72 : /* empty input, so return nothing */ 73 38 : node->grp_done = true; 74 38 : return NULL; 75 : } 76 : /* Copy tuple into firsttupleslot */ 77 122 : 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 122 : 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 122 : if (ExecQual(node->ss.ps.qual, econtext)) 90 : { 91 : /* 92 : * Form and return a projection tuple using the first input tuple. 93 : */ 94 122 : return ExecProject(node->ss.ps.ps_ProjInfo); 95 : } 96 : else 97 0 : 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 0 : for (;;) 106 : { 107 : /* 108 : * Scan over all remaining tuples that belong to this group 109 : */ 110 : for (;;) 111 : { 112 37688 : outerslot = ExecProcNode(outerPlanState(node)); 113 37688 : if (TupIsNull(outerslot)) 114 : { 115 : /* no more groups, so we're done */ 116 122 : node->grp_done = true; 117 122 : 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 37566 : econtext->ecxt_innertuple = firsttupleslot; 125 37566 : econtext->ecxt_outertuple = outerslot; 126 37566 : if (!ExecQualAndReset(node->eqfunction, econtext)) 127 7676 : 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 7676 : ExecCopySlot(firsttupleslot, outerslot); 136 7676 : 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 7676 : if (ExecQual(node->ss.ps.qual, econtext)) 143 : { 144 : /* 145 : * Form and return a projection tuple using the first input tuple. 146 : */ 147 7676 : return ExecProject(node->ss.ps.ps_ProjInfo); 148 : } 149 : else 150 0 : 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 222 : 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 222 : grpstate = makeNode(GroupState); 174 222 : grpstate->ss.ps.plan = (Plan *) node; 175 222 : grpstate->ss.ps.state = estate; 176 222 : grpstate->ss.ps.ExecProcNode = ExecGroup; 177 222 : grpstate->grp_done = false; 178 : 179 : /* 180 : * create expression context 181 : */ 182 222 : ExecAssignExprContext(estate, &grpstate->ss.ps); 183 : 184 : /* 185 : * initialize child nodes 186 : */ 187 222 : outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags); 188 : 189 : /* 190 : * Initialize scan slot and type. 191 : */ 192 222 : tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL); 193 222 : ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops); 194 : 195 : /* 196 : * Initialize result slot, type and projection. 197 : */ 198 222 : ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual); 199 222 : ExecAssignProjectionInfo(&grpstate->ss.ps, NULL); 200 : 201 : /* 202 : * initialize child expressions 203 : */ 204 222 : grpstate->ss.ps.qual = 205 222 : ExecInitQual(node->plan.qual, (PlanState *) grpstate); 206 : 207 : /* 208 : * Precompute fmgr lookup data for inner loop 209 : */ 210 222 : grpstate->eqfunction = 211 222 : execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)), 212 : node->numCols, 213 222 : node->grpColIdx, 214 222 : node->grpOperators, 215 222 : node->grpCollations, 216 : &grpstate->ss.ps); 217 : 218 222 : return grpstate; 219 : } 220 : 221 : /* ------------------------ 222 : * ExecEndGroup(node) 223 : * 224 : * ----------------------- 225 : */ 226 : void 227 222 : ExecEndGroup(GroupState *node) 228 : { 229 : PlanState *outerPlan; 230 : 231 222 : ExecFreeExprContext(&node->ss.ps); 232 : 233 : /* clean up tuple table */ 234 222 : ExecClearTuple(node->ss.ss_ScanTupleSlot); 235 : 236 222 : outerPlan = outerPlanState(node); 237 222 : ExecEndNode(outerPlan); 238 222 : } 239 : 240 : void 241 24 : ExecReScanGroup(GroupState *node) 242 : { 243 24 : PlanState *outerPlan = outerPlanState(node); 244 : 245 24 : node->grp_done = false; 246 : /* must clear first tuple */ 247 24 : 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 24 : if (outerPlan->chgParam == NULL) 254 24 : ExecReScan(outerPlan); 255 24 : }