1 /*-------------------------------------------------------------------------
2 *
3 * nodeGroup.c
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
5 *
6 * Portions Copyright (c) 1996-2018, 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 *
ExecGroup(PlanState * pstate)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 *
ExecInitGroup(Group * node,EState * estate,int eflags)162 ExecInitGroup(Group *node, EState *estate, int eflags)
163 {
164 GroupState *grpstate;
165
166 /* check for unsupported flags */
167 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
168
169 /*
170 * create state structure
171 */
172 grpstate = makeNode(GroupState);
173 grpstate->ss.ps.plan = (Plan *) node;
174 grpstate->ss.ps.state = estate;
175 grpstate->ss.ps.ExecProcNode = ExecGroup;
176 grpstate->grp_done = false;
177
178 /*
179 * create expression context
180 */
181 ExecAssignExprContext(estate, &grpstate->ss.ps);
182
183 /*
184 * initialize child nodes
185 */
186 outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
187
188 /*
189 * Initialize scan slot and type.
190 */
191 ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss);
192
193 /*
194 * Initialize result slot, type and projection.
195 */
196 ExecInitResultTupleSlotTL(estate, &grpstate->ss.ps);
197 ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
198
199 /*
200 * initialize child expressions
201 */
202 grpstate->ss.ps.qual =
203 ExecInitQual(node->plan.qual, (PlanState *) grpstate);
204
205 /*
206 * Precompute fmgr lookup data for inner loop
207 */
208 grpstate->eqfunction =
209 execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
210 node->numCols,
211 node->grpColIdx,
212 node->grpOperators,
213 &grpstate->ss.ps);
214
215 return grpstate;
216 }
217
218 /* ------------------------
219 * ExecEndGroup(node)
220 *
221 * -----------------------
222 */
223 void
ExecEndGroup(GroupState * node)224 ExecEndGroup(GroupState *node)
225 {
226 PlanState *outerPlan;
227
228 ExecFreeExprContext(&node->ss.ps);
229
230 /* clean up tuple table */
231 ExecClearTuple(node->ss.ss_ScanTupleSlot);
232
233 outerPlan = outerPlanState(node);
234 ExecEndNode(outerPlan);
235 }
236
237 void
ExecReScanGroup(GroupState * node)238 ExecReScanGroup(GroupState *node)
239 {
240 PlanState *outerPlan = outerPlanState(node);
241
242 node->grp_done = false;
243 /* must clear first tuple */
244 ExecClearTuple(node->ss.ss_ScanTupleSlot);
245
246 /*
247 * if chgParam of subnode is not null then plan will be re-scanned by
248 * first ExecProcNode.
249 */
250 if (outerPlan->chgParam == NULL)
251 ExecReScan(outerPlan);
252 }
253