1 /*-------------------------------------------------------------------------
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
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.
18 * src/backend/executor/nodeGroup.c
20 *-------------------------------------------------------------------------
25 #include "executor/executor.h"
26 #include "executor/nodeGroup.h"
27 #include "miscadmin.h"
33 * Return one tuple for each group of matching input tuples.
35 static TupleTableSlot *
36 ExecGroup(PlanState *pstate)
38 GroupState *node = castNode(GroupState, pstate);
39 ExprContext *econtext;
41 AttrNumber *grpColIdx;
42 TupleTableSlot *firsttupleslot;
43 TupleTableSlot *outerslot;
45 CHECK_FOR_INTERRUPTS();
48 * get state info from node
52 econtext = node->ss.ps.ps_ExprContext;
53 numCols = ((Group *) node->ss.ps.plan)->numCols;
54 grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
57 * The ScanTupleSlot holds the (copied) first tuple of each group.
59 firsttupleslot = node->ss.ss_ScanTupleSlot;
62 * We need not call ResetExprContext here because execTuplesMatch will
63 * reset the per-tuple memory context once per input tuple.
67 * If first time through, acquire first input tuple and determine whether
68 * to return it or not.
70 if (TupIsNull(firsttupleslot))
72 outerslot = ExecProcNode(outerPlanState(node));
73 if (TupIsNull(outerslot))
75 /* empty input, so return nothing */
76 node->grp_done = true;
79 /* Copy tuple into firsttupleslot */
80 ExecCopySlot(firsttupleslot, outerslot);
83 * Set it up as input for qual test and projection. The expressions
84 * will access the input tuple as varno OUTER.
86 econtext->ecxt_outertuple = firsttupleslot;
89 * Check the qual (HAVING clause); if the group does not match, ignore
90 * it and fall into scan loop.
92 if (ExecQual(node->ss.ps.qual, econtext))
95 * Form and return a projection tuple using the first input tuple.
97 return ExecProject(node->ss.ps.ps_ProjInfo);
100 InstrCountFiltered1(node, 1);
104 * This loop iterates once per input tuple group. At the head of the
105 * loop, we have finished processing the first tuple of the group and now
106 * need to scan over all the other group members.
111 * Scan over all remaining tuples that belong to this group
115 outerslot = ExecProcNode(outerPlanState(node));
116 if (TupIsNull(outerslot))
118 /* no more groups, so we're done */
119 node->grp_done = true;
124 * Compare with first tuple and see if this tuple is of the same
125 * group. If so, ignore it and keep scanning.
127 if (!execTuplesMatch(firsttupleslot, outerslot,
130 econtext->ecxt_per_tuple_memory))
135 * We have the first tuple of the next input group. See if we want to
138 /* Copy tuple, set up as input for qual test and projection */
139 ExecCopySlot(firsttupleslot, outerslot);
140 econtext->ecxt_outertuple = firsttupleslot;
143 * Check the qual (HAVING clause); if the group does not match, ignore
144 * it and loop back to scan the rest of the group.
146 if (ExecQual(node->ss.ps.qual, econtext))
149 * Form and return a projection tuple using the first input tuple.
151 return ExecProject(node->ss.ps.ps_ProjInfo);
154 InstrCountFiltered1(node, 1);
161 * Creates the run-time information for the group node produced by the
162 * planner and initializes its outer subtree
166 ExecInitGroup(Group *node, EState *estate, int eflags)
168 GroupState *grpstate;
170 /* check for unsupported flags */
171 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
174 * create state structure
176 grpstate = makeNode(GroupState);
177 grpstate->ss.ps.plan = (Plan *) node;
178 grpstate->ss.ps.state = estate;
179 grpstate->ss.ps.ExecProcNode = ExecGroup;
180 grpstate->grp_done = false;
183 * create expression context
185 ExecAssignExprContext(estate, &grpstate->ss.ps);
188 * tuple table initialization
190 ExecInitScanTupleSlot(estate, &grpstate->ss);
191 ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
194 * initialize child expressions
196 grpstate->ss.ps.qual =
197 ExecInitQual(node->plan.qual, (PlanState *) grpstate);
200 * initialize child nodes
202 outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
205 * initialize tuple type.
207 ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
210 * Initialize result tuple type and projection info.
212 ExecAssignResultTypeFromTL(&grpstate->ss.ps);
213 ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
216 * Precompute fmgr lookup data for inner loop
218 grpstate->eqfunctions =
219 execTuplesMatchPrepare(node->numCols,
225 /* ------------------------
228 * -----------------------
231 ExecEndGroup(GroupState *node)
233 PlanState *outerPlan;
235 ExecFreeExprContext(&node->ss.ps);
237 /* clean up tuple table */
238 ExecClearTuple(node->ss.ss_ScanTupleSlot);
240 outerPlan = outerPlanState(node);
241 ExecEndNode(outerPlan);
245 ExecReScanGroup(GroupState *node)
247 PlanState *outerPlan = outerPlanState(node);
249 node->grp_done = false;
250 /* must clear first tuple */
251 ExecClearTuple(node->ss.ss_ScanTupleSlot);
254 * if chgParam of subnode is not null then plan will be re-scanned by
255 * first ExecProcNode.
257 if (outerPlan->chgParam == NULL)
258 ExecReScan(outerPlan);