]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
bdc7fd8992a17c1de6dc138fee3c79301c0e5570
[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-2005, 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.59 2004/12/31 21:59:45 pgsql Exp $
19  *
20  *-------------------------------------------------------------------------
21  */
22
23 #include "postgres.h"
24
25 #include "access/heapam.h"
26 #include "executor/executor.h"
27 #include "executor/nodeGroup.h"
28
29
30 /*
31  *       ExecGroup -
32  *
33  *              Return one tuple for each group of matching input tuples.
34  */
35 TupleTableSlot *
36 ExecGroup(GroupState *node)
37 {
38         EState     *estate;
39         ExprContext *econtext;
40         TupleDesc       tupdesc;
41         int                     numCols;
42         AttrNumber *grpColIdx;
43         HeapTuple       outerTuple = NULL;
44         HeapTuple       firsttuple;
45         TupleTableSlot *outerslot;
46         ProjectionInfo *projInfo;
47         TupleTableSlot *resultSlot;
48
49         /*
50          * get state info from node
51          */
52         if (node->grp_done)
53                 return NULL;
54         estate = node->ss.ps.state;
55         econtext = node->ss.ps.ps_ExprContext;
56         tupdesc = ExecGetScanType(&node->ss);
57         numCols = ((Group *) node->ss.ps.plan)->numCols;
58         grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
59
60         /*
61          * We need not call ResetExprContext here because execTuplesMatch will
62          * reset the per-tuple memory context once per input tuple.
63          */
64
65         /* If we don't already have first tuple of group, fetch it */
66         /* this should occur on the first call only */
67         firsttuple = node->grp_firstTuple;
68         if (firsttuple == NULL)
69         {
70                 outerslot = ExecProcNode(outerPlanState(node));
71                 if (TupIsNull(outerslot))
72                 {
73                         node->grp_done = TRUE;
74                         return NULL;
75                 }
76                 node->grp_firstTuple = firsttuple =
77                         heap_copytuple(outerslot->val);
78         }
79
80         /*
81          * Scan over all tuples that belong to this group
82          */
83         for (;;)
84         {
85                 outerslot = ExecProcNode(outerPlanState(node));
86                 if (TupIsNull(outerslot))
87                 {
88                         node->grp_done = TRUE;
89                         outerTuple = NULL;
90                         break;
91                 }
92                 outerTuple = outerslot->val;
93
94                 /*
95                  * Compare with first tuple and see if this tuple is of the same
96                  * group.
97                  */
98                 if (!execTuplesMatch(firsttuple, outerTuple,
99                                                          tupdesc,
100                                                          numCols, grpColIdx,
101                                                          node->eqfunctions,
102                                                          econtext->ecxt_per_tuple_memory))
103                         break;
104         }
105
106         /*
107          * form a projection tuple based on the (copied) first tuple of the
108          * group, and store it in the result tuple slot.
109          */
110         ExecStoreTuple(firsttuple,
111                                    node->ss.ss_ScanTupleSlot,
112                                    InvalidBuffer,
113                                    false);
114         econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
115         projInfo = node->ss.ps.ps_ProjInfo;
116         resultSlot = ExecProject(projInfo, NULL);
117
118         /* save first tuple of next group, if we are not done yet */
119         if (!node->grp_done)
120         {
121                 heap_freetuple(firsttuple);
122                 node->grp_firstTuple = heap_copytuple(outerTuple);
123         }
124
125         return resultSlot;
126 }
127
128 /* -----------------
129  * ExecInitGroup
130  *
131  *      Creates the run-time information for the group node produced by the
132  *      planner and initializes its outer subtree
133  * -----------------
134  */
135 GroupState *
136 ExecInitGroup(Group *node, EState *estate)
137 {
138         GroupState *grpstate;
139
140         /*
141          * create state structure
142          */
143         grpstate = makeNode(GroupState);
144         grpstate->ss.ps.plan = (Plan *) node;
145         grpstate->ss.ps.state = estate;
146         grpstate->grp_firstTuple = NULL;
147         grpstate->grp_done = FALSE;
148
149         /*
150          * create expression context
151          */
152         ExecAssignExprContext(estate, &grpstate->ss.ps);
153
154 #define GROUP_NSLOTS 2
155
156         /*
157          * tuple table initialization
158          */
159         ExecInitScanTupleSlot(estate, &grpstate->ss);
160         ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
161
162         /*
163          * initialize child expressions
164          */
165         grpstate->ss.ps.targetlist = (List *)
166                 ExecInitExpr((Expr *) node->plan.targetlist,
167                                          (PlanState *) grpstate);
168         grpstate->ss.ps.qual = (List *)
169                 ExecInitExpr((Expr *) node->plan.qual,
170                                          (PlanState *) grpstate);
171
172         /*
173          * initialize child nodes
174          */
175         outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate);
176
177         /*
178          * initialize tuple type.
179          */
180         ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
181
182         /*
183          * Initialize result tuple type and projection info.
184          */
185         ExecAssignResultTypeFromTL(&grpstate->ss.ps);
186         ExecAssignProjectionInfo(&grpstate->ss.ps);
187
188         /*
189          * Precompute fmgr lookup data for inner loop
190          */
191         grpstate->eqfunctions =
192                 execTuplesMatchPrepare(ExecGetScanType(&grpstate->ss),
193                                                            node->numCols,
194                                                            node->grpColIdx);
195
196         return grpstate;
197 }
198
199 int
200 ExecCountSlotsGroup(Group *node)
201 {
202         return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
203 }
204
205 /* ------------------------
206  *              ExecEndGroup(node)
207  *
208  * -----------------------
209  */
210 void
211 ExecEndGroup(GroupState *node)
212 {
213         PlanState  *outerPlan;
214
215         ExecFreeExprContext(&node->ss.ps);
216
217         /* clean up tuple table */
218         ExecClearTuple(node->ss.ss_ScanTupleSlot);
219
220         outerPlan = outerPlanState(node);
221         ExecEndNode(outerPlan);
222 }
223
224 void
225 ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
226 {
227         node->grp_done = FALSE;
228         if (node->grp_firstTuple != NULL)
229         {
230                 heap_freetuple(node->grp_firstTuple);
231                 node->grp_firstTuple = NULL;
232         }
233
234         if (((PlanState *) node)->lefttree &&
235                 ((PlanState *) node)->lefttree->chgParam == NULL)
236                 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
237 }