]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Phase 1 of read-only-plans project: cause executor state nodes to point
[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-2002, 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  *        $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.51 2002/12/05 15:50:33 tgl Exp $
19  *
20  *-------------------------------------------------------------------------
21  */
22
23 #include "postgres.h"
24
25 #include "access/heapam.h"
26 #include "catalog/pg_operator.h"
27 #include "executor/executor.h"
28 #include "executor/nodeGroup.h"
29 #include "parser/parse_oper.h"
30 #include "utils/builtins.h"
31 #include "utils/lsyscache.h"
32 #include "utils/syscache.h"
33
34
35 /*
36  *       ExecGroup -
37  *
38  *              Return one tuple for each group of matching input tuples.
39  */
40 TupleTableSlot *
41 ExecGroup(GroupState *node)
42 {
43         EState     *estate;
44         ExprContext *econtext;
45         TupleDesc       tupdesc;
46         int                     numCols;
47         AttrNumber *grpColIdx;
48         HeapTuple       outerTuple = NULL;
49         HeapTuple       firsttuple;
50         TupleTableSlot *outerslot;
51         ProjectionInfo *projInfo;
52         TupleTableSlot *resultSlot;
53
54         /*
55          * get state info from node
56          */
57         if (node->grp_done)
58                 return NULL;
59         estate = node->ss.ps.state;
60         econtext = node->ss.ps.ps_ExprContext;
61         tupdesc = ExecGetScanType(&node->ss);
62         numCols = ((Group *) node->ss.ps.plan)->numCols;
63         grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
64
65         /*
66          * We need not call ResetExprContext here because execTuplesMatch will
67          * reset the per-tuple memory context once per input tuple.
68          */
69
70         /* If we don't already have first tuple of group, fetch it */
71         /* this should occur on the first call only */
72         firsttuple = node->grp_firstTuple;
73         if (firsttuple == NULL)
74         {
75                 outerslot = ExecProcNode(outerPlanState(node));
76                 if (TupIsNull(outerslot))
77                 {
78                         node->grp_done = TRUE;
79                         return NULL;
80                 }
81                 node->grp_firstTuple = firsttuple =
82                         heap_copytuple(outerslot->val);
83         }
84
85         /*
86          * Scan over all tuples that belong to this group
87          */
88         for (;;)
89         {
90                 outerslot = ExecProcNode(outerPlanState(node));
91                 if (TupIsNull(outerslot))
92                 {
93                         node->grp_done = TRUE;
94                         outerTuple = NULL;
95                         break;
96                 }
97                 outerTuple = outerslot->val;
98
99                 /*
100                  * Compare with first tuple and see if this tuple is of the same
101                  * group.
102                  */
103                 if (!execTuplesMatch(firsttuple, outerTuple,
104                                                          tupdesc,
105                                                          numCols, grpColIdx,
106                                                          node->eqfunctions,
107                                                          econtext->ecxt_per_tuple_memory))
108                         break;
109         }
110
111         /*
112          * form a projection tuple based on the (copied) first tuple of the
113          * group, and store it in the result tuple slot.
114          */
115         ExecStoreTuple(firsttuple,
116                                    node->ss.ss_ScanTupleSlot,
117                                    InvalidBuffer,
118                                    false);
119         econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
120         projInfo = node->ss.ps.ps_ProjInfo;
121         resultSlot = ExecProject(projInfo, NULL);
122
123         /* save first tuple of next group, if we are not done yet */
124         if (!node->grp_done)
125         {
126                 heap_freetuple(firsttuple);
127                 node->grp_firstTuple = heap_copytuple(outerTuple);
128         }
129
130         return resultSlot;
131 }
132
133 /* -----------------
134  * ExecInitGroup
135  *
136  *      Creates the run-time information for the group node produced by the
137  *      planner and initializes its outer subtree
138  * -----------------
139  */
140 GroupState *
141 ExecInitGroup(Group *node, EState *estate)
142 {
143         GroupState *grpstate;
144
145         /*
146          * create state structure
147          */
148         grpstate = makeNode(GroupState);
149         grpstate->ss.ps.plan = (Plan *) node;
150         grpstate->ss.ps.state = estate;
151         grpstate->grp_firstTuple = NULL;
152         grpstate->grp_done = FALSE;
153
154         /*
155          * create expression context
156          */
157         ExecAssignExprContext(estate, &grpstate->ss.ps);
158
159 #define GROUP_NSLOTS 2
160
161         /*
162          * tuple table initialization
163          */
164         ExecInitScanTupleSlot(estate, &grpstate->ss);
165         ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
166
167         /*
168          * initialize child expressions
169          */
170         grpstate->ss.ps.targetlist = (List *)
171                 ExecInitExpr((Node *) node->plan.targetlist,
172                                          (PlanState *) grpstate);
173         grpstate->ss.ps.qual = (List *)
174                 ExecInitExpr((Node *) node->plan.qual,
175                                          (PlanState *) grpstate);
176
177         /*
178          * initialize child nodes
179          */
180         outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate);
181
182         /*
183          * initialize tuple type.
184          */
185         ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
186
187         /*
188          * Initialize tuple type for both result and scan. This node does no
189          * projection
190          */
191         ExecAssignResultTypeFromTL(&grpstate->ss.ps);
192         ExecAssignProjectionInfo(&grpstate->ss.ps);
193
194         /*
195          * Precompute fmgr lookup data for inner loop
196          */
197         grpstate->eqfunctions =
198                 execTuplesMatchPrepare(ExecGetScanType(&grpstate->ss),
199                                                            node->numCols,
200                                                            node->grpColIdx);
201
202         return grpstate;
203 }
204
205 int
206 ExecCountSlotsGroup(Group *node)
207 {
208         return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
209 }
210
211 /* ------------------------
212  *              ExecEndGroup(node)
213  *
214  * -----------------------
215  */
216 void
217 ExecEndGroup(GroupState *node)
218 {
219         PlanState  *outerPlan;
220
221         ExecFreeProjectionInfo(&node->ss.ps);
222         ExecFreeExprContext(&node->ss.ps);
223
224         outerPlan = outerPlanState(node);
225         ExecEndNode(outerPlan);
226
227         /* clean up tuple table */
228         ExecClearTuple(node->ss.ss_ScanTupleSlot);
229         if (node->grp_firstTuple != NULL)
230         {
231                 heap_freetuple(node->grp_firstTuple);
232                 node->grp_firstTuple = NULL;
233         }
234 }
235
236 void
237 ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
238 {
239         node->grp_done = FALSE;
240         if (node->grp_firstTuple != NULL)
241         {
242                 heap_freetuple(node->grp_firstTuple);
243                 node->grp_firstTuple = NULL;
244         }
245
246         if (((PlanState *) node)->lefttree &&
247                 ((PlanState *) node)->lefttree->chgParam == NULL)
248                 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
249 }
250
251 /*****************************************************************************
252  *              Code shared with nodeUnique.c and nodeAgg.c
253  *****************************************************************************/
254
255 /*
256  * execTuplesMatch
257  *              Return true if two tuples match in all the indicated fields.
258  *              This is used to detect group boundaries in nodeGroup and nodeAgg,
259  *              and to decide whether two tuples are distinct or not in nodeUnique.
260  *
261  * tuple1, tuple2: the tuples to compare
262  * tupdesc: tuple descriptor applying to both tuples
263  * numCols: the number of attributes to be examined
264  * matchColIdx: array of attribute column numbers
265  * eqFunctions: array of fmgr lookup info for the equality functions to use
266  * evalContext: short-term memory context for executing the functions
267  *
268  * NB: evalContext is reset each time!
269  */
270 bool
271 execTuplesMatch(HeapTuple tuple1,
272                                 HeapTuple tuple2,
273                                 TupleDesc tupdesc,
274                                 int numCols,
275                                 AttrNumber *matchColIdx,
276                                 FmgrInfo *eqfunctions,
277                                 MemoryContext evalContext)
278 {
279         MemoryContext oldContext;
280         bool            result;
281         int                     i;
282
283         /* Reset and switch into the temp context. */
284         MemoryContextReset(evalContext);
285         oldContext = MemoryContextSwitchTo(evalContext);
286
287         /*
288          * We cannot report a match without checking all the fields, but we
289          * can report a non-match as soon as we find unequal fields.  So,
290          * start comparing at the last field (least significant sort key).
291          * That's the most likely to be different if we are dealing with
292          * sorted input.
293          */
294         result = true;
295
296         for (i = numCols; --i >= 0;)
297         {
298                 AttrNumber      att = matchColIdx[i];
299                 Datum           attr1,
300                                         attr2;
301                 bool            isNull1,
302                                         isNull2;
303
304                 attr1 = heap_getattr(tuple1,
305                                                          att,
306                                                          tupdesc,
307                                                          &isNull1);
308
309                 attr2 = heap_getattr(tuple2,
310                                                          att,
311                                                          tupdesc,
312                                                          &isNull2);
313
314                 if (isNull1 != isNull2)
315                 {
316                         result = false;         /* one null and one not; they aren't equal */
317                         break;
318                 }
319
320                 if (isNull1)
321                         continue;                       /* both are null, treat as equal */
322
323                 /* Apply the type-specific equality function */
324
325                 if (!DatumGetBool(FunctionCall2(&eqfunctions[i],
326                                                                                 attr1, attr2)))
327                 {
328                         result = false;         /* they aren't equal */
329                         break;
330                 }
331         }
332
333         MemoryContextSwitchTo(oldContext);
334
335         return result;
336 }
337
338 /*
339  * execTuplesMatchPrepare
340  *              Look up the equality functions needed for execTuplesMatch.
341  *              The result is a palloc'd array.
342  */
343 FmgrInfo *
344 execTuplesMatchPrepare(TupleDesc tupdesc,
345                                            int numCols,
346                                            AttrNumber *matchColIdx)
347 {
348         FmgrInfo   *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
349         int                     i;
350
351         for (i = 0; i < numCols; i++)
352         {
353                 AttrNumber      att = matchColIdx[i];
354                 Oid                     typid = tupdesc->attrs[att - 1]->atttypid;
355                 Oid                     eq_function;
356
357                 eq_function = equality_oper_funcid(typid);
358                 fmgr_info(eq_function, &eqfunctions[i]);
359         }
360
361         return eqfunctions;
362 }