]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Make NestLoop plan nodes pass outer-relation variables into their inner
[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-2010, 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  *        $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.78 2010/07/12 17:01:05 tgl Exp $
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         }
122
123         /*
124          * This loop iterates once per input tuple group.  At the head of the
125          * loop, we have finished processing the first tuple of the group and now
126          * need to scan over all the other group members.
127          */
128         for (;;)
129         {
130                 /*
131                  * Scan over all remaining tuples that belong to this group
132                  */
133                 for (;;)
134                 {
135                         outerslot = ExecProcNode(outerPlanState(node));
136                         if (TupIsNull(outerslot))
137                         {
138                                 /* no more groups, so we're done */
139                                 node->grp_done = TRUE;
140                                 return NULL;
141                         }
142
143                         /*
144                          * Compare with first tuple and see if this tuple is of the same
145                          * group.  If so, ignore it and keep scanning.
146                          */
147                         if (!execTuplesMatch(firsttupleslot, outerslot,
148                                                                  numCols, grpColIdx,
149                                                                  node->eqfunctions,
150                                                                  econtext->ecxt_per_tuple_memory))
151                                 break;
152                 }
153
154                 /*
155                  * We have the first tuple of the next input group.  See if we want to
156                  * return it.
157                  */
158                 /* Copy tuple, set up as input for qual test and projection */
159                 ExecCopySlot(firsttupleslot, outerslot);
160                 econtext->ecxt_outertuple = firsttupleslot;
161
162                 /*
163                  * Check the qual (HAVING clause); if the group does not match, ignore
164                  * it and loop back to scan the rest of the group.
165                  */
166                 if (ExecQual(node->ss.ps.qual, econtext, false))
167                 {
168                         /*
169                          * Form and return a projection tuple using the first input tuple.
170                          */
171                         TupleTableSlot *result;
172                         ExprDoneCond isDone;
173
174                         result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
175
176                         if (isDone != ExprEndResult)
177                         {
178                                 node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
179                                 return result;
180                         }
181                 }
182         }
183
184         /* NOTREACHED */
185         return NULL;
186 }
187
188 /* -----------------
189  * ExecInitGroup
190  *
191  *      Creates the run-time information for the group node produced by the
192  *      planner and initializes its outer subtree
193  * -----------------
194  */
195 GroupState *
196 ExecInitGroup(Group *node, EState *estate, int eflags)
197 {
198         GroupState *grpstate;
199
200         /* check for unsupported flags */
201         Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
202
203         /*
204          * create state structure
205          */
206         grpstate = makeNode(GroupState);
207         grpstate->ss.ps.plan = (Plan *) node;
208         grpstate->ss.ps.state = estate;
209         grpstate->grp_done = FALSE;
210
211         /*
212          * create expression context
213          */
214         ExecAssignExprContext(estate, &grpstate->ss.ps);
215
216         /*
217          * tuple table initialization
218          */
219         ExecInitScanTupleSlot(estate, &grpstate->ss);
220         ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
221
222         /*
223          * initialize child expressions
224          */
225         grpstate->ss.ps.targetlist = (List *)
226                 ExecInitExpr((Expr *) node->plan.targetlist,
227                                          (PlanState *) grpstate);
228         grpstate->ss.ps.qual = (List *)
229                 ExecInitExpr((Expr *) node->plan.qual,
230                                          (PlanState *) grpstate);
231
232         /*
233          * initialize child nodes
234          */
235         outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
236
237         /*
238          * initialize tuple type.
239          */
240         ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
241
242         /*
243          * Initialize result tuple type and projection info.
244          */
245         ExecAssignResultTypeFromTL(&grpstate->ss.ps);
246         ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
247
248         grpstate->ss.ps.ps_TupFromTlist = false;
249
250         /*
251          * Precompute fmgr lookup data for inner loop
252          */
253         grpstate->eqfunctions =
254                 execTuplesMatchPrepare(node->numCols,
255                                                            node->grpOperators);
256
257         return grpstate;
258 }
259
260 /* ------------------------
261  *              ExecEndGroup(node)
262  *
263  * -----------------------
264  */
265 void
266 ExecEndGroup(GroupState *node)
267 {
268         PlanState  *outerPlan;
269
270         ExecFreeExprContext(&node->ss.ps);
271
272         /* clean up tuple table */
273         ExecClearTuple(node->ss.ss_ScanTupleSlot);
274
275         outerPlan = outerPlanState(node);
276         ExecEndNode(outerPlan);
277 }
278
279 void
280 ExecReScanGroup(GroupState *node)
281 {
282         node->grp_done = FALSE;
283         node->ss.ps.ps_TupFromTlist = false;
284         /* must clear first tuple */
285         ExecClearTuple(node->ss.ss_ScanTupleSlot);
286
287         /*
288          * if chgParam of subnode is not null then plan will be re-scanned by
289          * first ExecProcNode.
290          */
291         if (node->ss.ps.lefttree &&
292                 node->ss.ps.lefttree->chgParam == NULL)
293                 ExecReScan(node->ss.ps.lefttree);
294 }