]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Update copyright for 2015
[postgresql] / src / backend / executor / nodeGroup.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeGroup.c
4  *        Routines to handle group nodes (used for queries with GROUP BY clause).
5  *
6  * Portions Copyright (c) 1996-2015, 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 *
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 *
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
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
281 ExecReScanGroup(GroupState *node)
282 {
283         node->grp_done = FALSE;
284         node->ss.ps.ps_TupFromTlist = false;
285         /* must clear first tuple */
286         ExecClearTuple(node->ss.ss_ScanTupleSlot);
287
288         /*
289          * if chgParam of subnode is not null then plan will be re-scanned by
290          * first ExecProcNode.
291          */
292         if (node->ss.ps.lefttree &&
293                 node->ss.ps.lefttree->chgParam == NULL)
294                 ExecReScan(node->ss.ps.lefttree);
295 }