]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Fix an oversight in two different recent patches: nodes that support SRFs
[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-2008, 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.72 2008/10/23 15:29:23 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 #define GROUP_NSLOTS 2
217
218         /*
219          * tuple table initialization
220          */
221         ExecInitScanTupleSlot(estate, &grpstate->ss);
222         ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
223
224         /*
225          * initialize child expressions
226          */
227         grpstate->ss.ps.targetlist = (List *)
228                 ExecInitExpr((Expr *) node->plan.targetlist,
229                                          (PlanState *) grpstate);
230         grpstate->ss.ps.qual = (List *)
231                 ExecInitExpr((Expr *) node->plan.qual,
232                                          (PlanState *) grpstate);
233
234         /*
235          * initialize child nodes
236          */
237         outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
238
239         /*
240          * initialize tuple type.
241          */
242         ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
243
244         /*
245          * Initialize result tuple type and projection info.
246          */
247         ExecAssignResultTypeFromTL(&grpstate->ss.ps);
248         ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
249
250         grpstate->ss.ps.ps_TupFromTlist = false;
251
252         /*
253          * Precompute fmgr lookup data for inner loop
254          */
255         grpstate->eqfunctions =
256                 execTuplesMatchPrepare(node->numCols,
257                                                            node->grpOperators);
258
259         return grpstate;
260 }
261
262 int
263 ExecCountSlotsGroup(Group *node)
264 {
265         return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
266 }
267
268 /* ------------------------
269  *              ExecEndGroup(node)
270  *
271  * -----------------------
272  */
273 void
274 ExecEndGroup(GroupState *node)
275 {
276         PlanState  *outerPlan;
277
278         ExecFreeExprContext(&node->ss.ps);
279
280         /* clean up tuple table */
281         ExecClearTuple(node->ss.ss_ScanTupleSlot);
282
283         outerPlan = outerPlanState(node);
284         ExecEndNode(outerPlan);
285 }
286
287 void
288 ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
289 {
290         node->grp_done = FALSE;
291         node->ss.ps.ps_TupFromTlist = false;
292         /* must clear first tuple */
293         ExecClearTuple(node->ss.ss_ScanTupleSlot);
294
295         if (((PlanState *) node)->lefttree &&
296                 ((PlanState *) node)->lefttree->chgParam == NULL)
297                 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
298 }