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 | |