]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Change the planner-to-executor API so that the planner tells the executor
[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-2007, 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.67 2007/01/10 18:06:02 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          * The ScanTupleSlot holds the (copied) first tuple of each group.
54          */
55         firsttupleslot = node->ss.ss_ScanTupleSlot;
56
57         /*
58          * We need not call ResetExprContext here because execTuplesMatch will
59          * reset the per-tuple memory context once per input tuple.
60          */
61
62         /*
63          * If first time through, acquire first input tuple and determine whether
64          * to return it or not.
65          */
66         if (TupIsNull(firsttupleslot))
67         {
68                 outerslot = ExecProcNode(outerPlanState(node));
69                 if (TupIsNull(outerslot))
70                 {
71                         /* empty input, so return nothing */
72                         node->grp_done = TRUE;
73                         return NULL;
74                 }
75                 /* Copy tuple, set up as input for qual test and projection */
76                 ExecCopySlot(firsttupleslot, outerslot);
77                 econtext->ecxt_scantuple = firsttupleslot;
78
79                 /*
80                  * Check the qual (HAVING clause); if the group does not match, ignore
81                  * it and fall into scan loop.
82                  */
83                 if (ExecQual(node->ss.ps.qual, econtext, false))
84                 {
85                         /*
86                          * Form and return a projection tuple using the first input tuple.
87                          */
88                         return ExecProject(node->ss.ps.ps_ProjInfo, NULL);
89                 }
90         }
91
92         /*
93          * This loop iterates once per input tuple group.  At the head of the
94          * loop, we have finished processing the first tuple of the group and now
95          * need to scan over all the other group members.
96          */
97         for (;;)
98         {
99                 /*
100                  * Scan over all remaining tuples that belong to this group
101                  */
102                 for (;;)
103                 {
104                         outerslot = ExecProcNode(outerPlanState(node));
105                         if (TupIsNull(outerslot))
106                         {
107                                 /* no more groups, so we're done */
108                                 node->grp_done = TRUE;
109                                 return NULL;
110                         }
111
112                         /*
113                          * Compare with first tuple and see if this tuple is of the same
114                          * group.  If so, ignore it and keep scanning.
115                          */
116                         if (!execTuplesMatch(firsttupleslot, outerslot,
117                                                                  numCols, grpColIdx,
118                                                                  node->eqfunctions,
119                                                                  econtext->ecxt_per_tuple_memory))
120                                 break;
121                 }
122
123                 /*
124                  * We have the first tuple of the next input group.  See if we want to
125                  * return it.
126                  */
127                 /* Copy tuple, set up as input for qual test and projection */
128                 ExecCopySlot(firsttupleslot, outerslot);
129                 econtext->ecxt_scantuple = firsttupleslot;
130
131                 /*
132                  * Check the qual (HAVING clause); if the group does not match, ignore
133                  * it and loop back to scan the rest of the group.
134                  */
135                 if (ExecQual(node->ss.ps.qual, econtext, false))
136                 {
137                         /*
138                          * Form and return a projection tuple using the first input tuple.
139                          */
140                         return ExecProject(node->ss.ps.ps_ProjInfo, NULL);
141                 }
142         }
143
144         /* NOTREACHED */
145         return NULL;
146 }
147
148 /* -----------------
149  * ExecInitGroup
150  *
151  *      Creates the run-time information for the group node produced by the
152  *      planner and initializes its outer subtree
153  * -----------------
154  */
155 GroupState *
156 ExecInitGroup(Group *node, EState *estate, int eflags)
157 {
158         GroupState *grpstate;
159
160         /* check for unsupported flags */
161         Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
162
163         /*
164          * create state structure
165          */
166         grpstate = makeNode(GroupState);
167         grpstate->ss.ps.plan = (Plan *) node;
168         grpstate->ss.ps.state = estate;
169         grpstate->grp_done = FALSE;
170
171         /*
172          * create expression context
173          */
174         ExecAssignExprContext(estate, &grpstate->ss.ps);
175
176 #define GROUP_NSLOTS 2
177
178         /*
179          * tuple table initialization
180          */
181         ExecInitScanTupleSlot(estate, &grpstate->ss);
182         ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
183
184         /*
185          * initialize child expressions
186          */
187         grpstate->ss.ps.targetlist = (List *)
188                 ExecInitExpr((Expr *) node->plan.targetlist,
189                                          (PlanState *) grpstate);
190         grpstate->ss.ps.qual = (List *)
191                 ExecInitExpr((Expr *) node->plan.qual,
192                                          (PlanState *) grpstate);
193
194         /*
195          * initialize child nodes
196          */
197         outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
198
199         /*
200          * initialize tuple type.
201          */
202         ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
203
204         /*
205          * Initialize result tuple type and projection info.
206          */
207         ExecAssignResultTypeFromTL(&grpstate->ss.ps);
208         ExecAssignProjectionInfo(&grpstate->ss.ps);
209
210         /*
211          * Precompute fmgr lookup data for inner loop
212          */
213         grpstate->eqfunctions =
214                 execTuplesMatchPrepare(node->numCols,
215                                                            node->grpOperators);
216
217         return grpstate;
218 }
219
220 int
221 ExecCountSlotsGroup(Group *node)
222 {
223         return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
224 }
225
226 /* ------------------------
227  *              ExecEndGroup(node)
228  *
229  * -----------------------
230  */
231 void
232 ExecEndGroup(GroupState *node)
233 {
234         PlanState  *outerPlan;
235
236         ExecFreeExprContext(&node->ss.ps);
237
238         /* clean up tuple table */
239         ExecClearTuple(node->ss.ss_ScanTupleSlot);
240
241         outerPlan = outerPlanState(node);
242         ExecEndNode(outerPlan);
243 }
244
245 void
246 ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
247 {
248         node->grp_done = FALSE;
249         /* must clear first tuple */
250         ExecClearTuple(node->ss.ss_ScanTupleSlot);
251
252         if (((PlanState *) node)->lefttree &&
253                 ((PlanState *) node)->lefttree->chgParam == NULL)
254                 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
255 }