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