1 /*-------------------------------------------------------------------------
2 *
3 * nodeGroup.c
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
5 *
6 * Portions Copyright (c) 1996-2016, 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
28
29 /*
30 * ExecGroup -
31 *
32 * Return one tuple for each group of matching input tuples.
33 */
34 TupleTableSlot *
ExecGroup(GroupState * node)35 ExecGroup(GroupState *node)
36 {
37 ExprContext *econtext;
38 int numCols;
39 AttrNumber *grpColIdx;
40 TupleTableSlot *firsttupleslot;
41 TupleTableSlot *outerslot;
42
43 /*
44 * get state info from node
45 */
46 if (node->grp_done)
47 return NULL;
48 econtext = node->ss.ps.ps_ExprContext;
49 numCols = ((Group *) node->ss.ps.plan)->numCols;
50 grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
51
52 /*
53 * Check to see if we're still projecting out tuples from a previous group
54 * tuple (because there is a function-returning-set in the projection
55 * expressions). If so, try to project another one.
56 */
57 if (node->ss.ps.ps_TupFromTlist)
58 {
59 TupleTableSlot *result;
60 ExprDoneCond isDone;
61
62 result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
63 if (isDone == ExprMultipleResult)
64 return result;
65 /* Done with that source tuple... */
66 node->ss.ps.ps_TupFromTlist = false;
67 }
68
69 /*
70 * The ScanTupleSlot holds the (copied) first tuple of each group.
71 */
72 firsttupleslot = node->ss.ss_ScanTupleSlot;
73
74 /*
75 * We need not call ResetExprContext here because execTuplesMatch will
76 * reset the per-tuple memory context once per input tuple.
77 */
78
79 /*
80 * If first time through, acquire first input tuple and determine whether
81 * to return it or not.
82 */
83 if (TupIsNull(firsttupleslot))
84 {
85 outerslot = ExecProcNode(outerPlanState(node));
86 if (TupIsNull(outerslot))
87 {
88 /* empty input, so return nothing */
89 node->grp_done = TRUE;
90 return NULL;
91 }
92 /* Copy tuple into firsttupleslot */
93 ExecCopySlot(firsttupleslot, outerslot);
94
95 /*
96 * Set it up as input for qual test and projection. The expressions
97 * will access the input tuple as varno OUTER.
98 */
99 econtext->ecxt_outertuple = firsttupleslot;
100
101 /*
102 * Check the qual (HAVING clause); if the group does not match, ignore
103 * it and fall into scan loop.
104 */
105 if (ExecQual(node->ss.ps.qual, econtext, false))
106 {
107 /*
108 * Form and return a projection tuple using the first input tuple.
109 */
110 TupleTableSlot *result;
111 ExprDoneCond isDone;
112
113 result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
114
115 if (isDone != ExprEndResult)
116 {
117 node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
118 return result;
119 }
120 }
121 else
122 InstrCountFiltered1(node, 1);
123 }
124
125 /*
126 * This loop iterates once per input tuple group. At the head of the
127 * loop, we have finished processing the first tuple of the group and now
128 * need to scan over all the other group members.
129 */
130 for (;;)
131 {
132 /*
133 * Scan over all remaining tuples that belong to this group
134 */
135 for (;;)
136 {
137 outerslot = ExecProcNode(outerPlanState(node));
138 if (TupIsNull(outerslot))
139 {
140 /* no more groups, so we're done */
141 node->grp_done = TRUE;
142 return NULL;
143 }
144
145 /*
146 * Compare with first tuple and see if this tuple is of the same
147 * group. If so, ignore it and keep scanning.
148 */
149 if (!execTuplesMatch(firsttupleslot, outerslot,
150 numCols, grpColIdx,
151 node->eqfunctions,
152 econtext->ecxt_per_tuple_memory))
153 break;
154 }
155
156 /*
157 * We have the first tuple of the next input group. See if we want to
158 * return it.
159 */
160 /* Copy tuple, set up as input for qual test and projection */
161 ExecCopySlot(firsttupleslot, outerslot);
162 econtext->ecxt_outertuple = firsttupleslot;
163
164 /*
165 * Check the qual (HAVING clause); if the group does not match, ignore
166 * it and loop back to scan the rest of the group.
167 */
168 if (ExecQual(node->ss.ps.qual, econtext, false))
169 {
170 /*
171 * Form and return a projection tuple using the first input tuple.
172 */
173 TupleTableSlot *result;
174 ExprDoneCond isDone;
175
176 result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
177
178 if (isDone != ExprEndResult)
179 {
180 node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
181 return result;
182 }
183 }
184 else
185 InstrCountFiltered1(node, 1);
186 }
187 }
188
189 /* -----------------
190 * ExecInitGroup
191 *
192 * Creates the run-time information for the group node produced by the
193 * planner and initializes its outer subtree
194 * -----------------
195 */
196 GroupState *
ExecInitGroup(Group * node,EState * estate,int eflags)197 ExecInitGroup(Group *node, EState *estate, int eflags)
198 {
199 GroupState *grpstate;
200
201 /* check for unsupported flags */
202 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
203
204 /*
205 * create state structure
206 */
207 grpstate = makeNode(GroupState);
208 grpstate->ss.ps.plan = (Plan *) node;
209 grpstate->ss.ps.state = estate;
210 grpstate->grp_done = FALSE;
211
212 /*
213 * create expression context
214 */
215 ExecAssignExprContext(estate, &grpstate->ss.ps);
216
217 /*
218 * tuple table initialization
219 */
220 ExecInitScanTupleSlot(estate, &grpstate->ss);
221 ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
222
223 /*
224 * initialize child expressions
225 */
226 grpstate->ss.ps.targetlist = (List *)
227 ExecInitExpr((Expr *) node->plan.targetlist,
228 (PlanState *) grpstate);
229 grpstate->ss.ps.qual = (List *)
230 ExecInitExpr((Expr *) node->plan.qual,
231 (PlanState *) grpstate);
232
233 /*
234 * initialize child nodes
235 */
236 outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
237
238 /*
239 * initialize tuple type.
240 */
241 ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
242
243 /*
244 * Initialize result tuple type and projection info.
245 */
246 ExecAssignResultTypeFromTL(&grpstate->ss.ps);
247 ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
248
249 grpstate->ss.ps.ps_TupFromTlist = false;
250
251 /*
252 * Precompute fmgr lookup data for inner loop
253 */
254 grpstate->eqfunctions =
255 execTuplesMatchPrepare(node->numCols,
256 node->grpOperators);
257
258 return grpstate;
259 }
260
261 /* ------------------------
262 * ExecEndGroup(node)
263 *
264 * -----------------------
265 */
266 void
ExecEndGroup(GroupState * node)267 ExecEndGroup(GroupState *node)
268 {
269 PlanState *outerPlan;
270
271 ExecFreeExprContext(&node->ss.ps);
272
273 /* clean up tuple table */
274 ExecClearTuple(node->ss.ss_ScanTupleSlot);
275
276 outerPlan = outerPlanState(node);
277 ExecEndNode(outerPlan);
278 }
279
280 void
ExecReScanGroup(GroupState * node)281 ExecReScanGroup(GroupState *node)
282 {
283 PlanState *outerPlan = outerPlanState(node);
284
285 node->grp_done = FALSE;
286 node->ss.ps.ps_TupFromTlist = false;
287 /* must clear first tuple */
288 ExecClearTuple(node->ss.ss_ScanTupleSlot);
289
290 /*
291 * if chgParam of subnode is not null then plan will be re-scanned by
292 * first ExecProcNode.
293 */
294 if (outerPlan->chgParam == NULL)
295 ExecReScan(outerPlan);
296 }
297