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