]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeGroup.c
Change Copyright from PostgreSQL, Inc to PostgreSQL Global Development Group.
[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-2001, 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.40 2001/01/24 19:42:54 momjian 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 "parser/parse_type.h"
31 #include "utils/lsyscache.h"
32 #include "utils/syscache.h"
33
34 static TupleTableSlot *ExecGroupEveryTuple(Group *node);
35 static TupleTableSlot *ExecGroupOneTuple(Group *node);
36
37 /* ---------------------------------------
38  *       ExecGroup -
39  *
40  *              There are two modes in which tuples are returned by ExecGroup. If
41  *              tuplePerGroup is TRUE, every tuple from the same group will be
42  *              returned, followed by a NULL at the end of each group. This is
43  *              useful for Agg node which needs to aggregate over tuples of the same
44  *              group. (eg. SELECT salary, count(*) FROM emp GROUP BY salary)
45  *
46  *              If tuplePerGroup is FALSE, only one tuple per group is returned. The
47  *              tuple returned contains only the group columns. NULL is returned only
48  *              at the end when no more groups are present. This is useful when
49  *              the query does not involve aggregates. (eg. SELECT salary FROM emp
50  *              GROUP BY salary)
51  * ------------------------------------------
52  */
53 TupleTableSlot *
54 ExecGroup(Group *node)
55 {
56         if (node->tuplePerGroup)
57                 return ExecGroupEveryTuple(node);
58         else
59                 return ExecGroupOneTuple(node);
60 }
61
62 /*
63  * ExecGroupEveryTuple -
64  *       return every tuple with a NULL between each group
65  */
66 static TupleTableSlot *
67 ExecGroupEveryTuple(Group *node)
68 {
69         GroupState *grpstate;
70         EState     *estate;
71         ExprContext *econtext;
72         TupleDesc       tupdesc;
73         HeapTuple       outerTuple = NULL;
74         HeapTuple       firsttuple;
75         TupleTableSlot *outerslot;
76         ProjectionInfo *projInfo;
77         TupleTableSlot *resultSlot;
78
79         /* ---------------------
80          *      get state info from node
81          * ---------------------
82          */
83         grpstate = node->grpstate;
84         if (grpstate->grp_done)
85                 return NULL;
86         estate = node->plan.state;
87         econtext = grpstate->csstate.cstate.cs_ExprContext;
88         tupdesc = ExecGetScanType(&grpstate->csstate);
89
90         /*
91          *      We need not call ResetExprContext here because execTuplesMatch
92          *      will reset the per-tuple memory context once per input tuple.
93          */
94
95         /* if we haven't returned first tuple of a new group yet ... */
96         if (grpstate->grp_useFirstTuple)
97         {
98                 grpstate->grp_useFirstTuple = FALSE;
99
100                 /*
101                  * note we rely on subplan to hold ownership of the tuple for as
102                  * long as we need it; we don't copy it.
103                  */
104                 ExecStoreTuple(grpstate->grp_firstTuple,
105                                            grpstate->csstate.css_ScanTupleSlot,
106                                            InvalidBuffer, false);
107         }
108         else
109         {
110                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
111                 if (TupIsNull(outerslot))
112                 {
113                         grpstate->grp_done = TRUE;
114                         return NULL;
115                 }
116                 outerTuple = outerslot->val;
117
118                 firsttuple = grpstate->grp_firstTuple;
119                 if (firsttuple == NULL)
120                 {
121                         /* this should occur on the first call only */
122                         grpstate->grp_firstTuple = heap_copytuple(outerTuple);
123                 }
124                 else
125                 {
126
127                         /*
128                          * Compare with first tuple and see if this tuple is of the
129                          * same group.
130                          */
131                         if (!execTuplesMatch(firsttuple, outerTuple,
132                                                                  tupdesc,
133                                                                  node->numCols, node->grpColIdx,
134                                                                  grpstate->eqfunctions,
135                                                                  econtext->ecxt_per_tuple_memory))
136                         {
137
138                                 /*
139                                  * No; save the tuple to return it next time, and return
140                                  * NULL
141                                  */
142                                 grpstate->grp_useFirstTuple = TRUE;
143                                 heap_freetuple(firsttuple);
144                                 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
145
146                                 return NULL;    /* signifies the end of the group */
147                         }
148                 }
149
150                 /*
151                  * note we rely on subplan to hold ownership of the tuple for as
152                  * long as we need it; we don't copy it.
153                  */
154                 ExecStoreTuple(outerTuple,
155                                            grpstate->csstate.css_ScanTupleSlot,
156                                            InvalidBuffer, false);
157         }
158
159         /* ----------------
160          *      form a projection tuple, store it in the result tuple
161          *      slot and return it.
162          * ----------------
163          */
164         projInfo = grpstate->csstate.cstate.cs_ProjInfo;
165
166         econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
167         resultSlot = ExecProject(projInfo, NULL);
168
169         return resultSlot;
170 }
171
172 /*
173  * ExecGroupOneTuple -
174  *        returns one tuple per group, a NULL at the end when there are no more
175  *        tuples.
176  */
177 static TupleTableSlot *
178 ExecGroupOneTuple(Group *node)
179 {
180         GroupState *grpstate;
181         EState     *estate;
182         ExprContext *econtext;
183         TupleDesc       tupdesc;
184         HeapTuple       outerTuple = NULL;
185         HeapTuple       firsttuple;
186         TupleTableSlot *outerslot;
187         ProjectionInfo *projInfo;
188         TupleTableSlot *resultSlot;
189
190         /* ---------------------
191          *      get state info from node
192          * ---------------------
193          */
194         grpstate = node->grpstate;
195         if (grpstate->grp_done)
196                 return NULL;
197         estate = node->plan.state;
198         econtext = node->grpstate->csstate.cstate.cs_ExprContext;
199         tupdesc = ExecGetScanType(&grpstate->csstate);
200
201         /*
202          *      We need not call ResetExprContext here because execTuplesMatch
203          *      will reset the per-tuple memory context once per input tuple.
204          */
205
206         firsttuple = grpstate->grp_firstTuple;
207         if (firsttuple == NULL)
208         {
209                 /* this should occur on the first call only */
210                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
211                 if (TupIsNull(outerslot))
212                 {
213                         grpstate->grp_done = TRUE;
214                         return NULL;
215                 }
216                 grpstate->grp_firstTuple = firsttuple =
217                         heap_copytuple(outerslot->val);
218         }
219
220         /*
221          * find all tuples that belong to a group
222          */
223         for (;;)
224         {
225                 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
226                 if (TupIsNull(outerslot))
227                 {
228                         grpstate->grp_done = TRUE;
229                         outerTuple = NULL;
230                         break;
231                 }
232                 outerTuple = outerslot->val;
233
234                 /*
235                  * Compare with first tuple and see if this tuple is of the same
236                  * group.
237                  */
238                 if (!execTuplesMatch(firsttuple, outerTuple,
239                                                          tupdesc,
240                                                          node->numCols, node->grpColIdx,
241                                                          grpstate->eqfunctions,
242                                                          econtext->ecxt_per_tuple_memory))
243                         break;
244         }
245
246         /* ----------------
247          *      form a projection tuple, store it in the result tuple
248          *      slot and return it.
249          * ----------------
250          */
251         projInfo = grpstate->csstate.cstate.cs_ProjInfo;
252
253         /*
254          * note we rely on subplan to hold ownership of the tuple for as long
255          * as we need it; we don't copy it.
256          */
257         ExecStoreTuple(firsttuple,
258                                    grpstate->csstate.css_ScanTupleSlot,
259                                    InvalidBuffer, false);
260         econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
261         resultSlot = ExecProject(projInfo, NULL);
262
263         /* save outerTuple if we are not done yet */
264         if (!grpstate->grp_done)
265         {
266                 heap_freetuple(firsttuple);
267                 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
268         }
269
270         return resultSlot;
271 }
272
273 /* -----------------
274  * ExecInitGroup
275  *
276  *      Creates the run-time information for the group node produced by the
277  *      planner and initializes its outer subtree
278  * -----------------
279  */
280 bool
281 ExecInitGroup(Group *node, EState *estate, Plan *parent)
282 {
283         GroupState *grpstate;
284         Plan       *outerPlan;
285
286         /*
287          * assign the node's execution state
288          */
289         node->plan.state = estate;
290
291         /*
292          * create state structure
293          */
294         grpstate = makeNode(GroupState);
295         node->grpstate = grpstate;
296         grpstate->grp_useFirstTuple = FALSE;
297         grpstate->grp_done = FALSE;
298         grpstate->grp_firstTuple = NULL;
299
300         /*
301          * create expression context
302          */
303         ExecAssignExprContext(estate, &grpstate->csstate.cstate);
304
305 #define GROUP_NSLOTS 2
306
307         /*
308          * tuple table initialization
309          */
310         ExecInitScanTupleSlot(estate, &grpstate->csstate);
311         ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
312
313         /*
314          * initializes child nodes
315          */
316         outerPlan = outerPlan(node);
317         ExecInitNode(outerPlan, estate, (Plan *) node);
318
319         /* ----------------
320          *      initialize tuple type.
321          * ----------------
322          */
323         ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
324
325         /*
326          * Initialize tuple type for both result and scan. This node does no
327          * projection
328          */
329         ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
330         ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
331
332         /*
333          * Precompute fmgr lookup data for inner loop
334          */
335         grpstate->eqfunctions =
336                 execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
337                                                            node->numCols,
338                                                            node->grpColIdx);
339
340         return TRUE;
341 }
342
343 int
344 ExecCountSlotsGroup(Group *node)
345 {
346         return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
347 }
348
349 /* ------------------------
350  *              ExecEndGroup(node)
351  *
352  * -----------------------
353  */
354 void
355 ExecEndGroup(Group *node)
356 {
357         GroupState *grpstate;
358         Plan       *outerPlan;
359
360         grpstate = node->grpstate;
361
362         ExecFreeProjectionInfo(&grpstate->csstate.cstate);
363         ExecFreeExprContext(&grpstate->csstate.cstate);
364
365         outerPlan = outerPlan(node);
366         ExecEndNode(outerPlan, (Plan *) node);
367
368         /* clean up tuple table */
369         ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
370         if (grpstate->grp_firstTuple != NULL)
371         {
372                 heap_freetuple(grpstate->grp_firstTuple);
373                 grpstate->grp_firstTuple = NULL;
374         }
375 }
376
377 void
378 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
379 {
380         GroupState *grpstate = node->grpstate;
381
382         grpstate->grp_useFirstTuple = FALSE;
383         grpstate->grp_done = FALSE;
384         if (grpstate->grp_firstTuple != NULL)
385         {
386                 heap_freetuple(grpstate->grp_firstTuple);
387                 grpstate->grp_firstTuple = NULL;
388         }
389
390         if (((Plan *) node)->lefttree &&
391                 ((Plan *) node)->lefttree->chgParam == NULL)
392                 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
393 }
394
395 /*****************************************************************************
396  *              Code shared with nodeUnique.c
397  *****************************************************************************/
398
399 /*
400  * execTuplesMatch
401  *              Return true if two tuples match in all the indicated fields.
402  *              This is used to detect group boundaries in nodeGroup, and to
403  *              decide whether two tuples are distinct or not in nodeUnique.
404  *
405  * tuple1, tuple2: the tuples to compare
406  * tupdesc: tuple descriptor applying to both tuples
407  * numCols: the number of attributes to be examined
408  * matchColIdx: array of attribute column numbers
409  * eqFunctions: array of fmgr lookup info for the equality functions to use
410  * evalContext: short-term memory context for executing the functions
411  *
412  * NB: evalContext is reset each time!
413  */
414 bool
415 execTuplesMatch(HeapTuple tuple1,
416                                 HeapTuple tuple2,
417                                 TupleDesc tupdesc,
418                                 int numCols,
419                                 AttrNumber *matchColIdx,
420                                 FmgrInfo *eqfunctions,
421                                 MemoryContext evalContext)
422 {
423         MemoryContext oldContext;
424         bool            result;
425         int                     i;
426
427         /* Reset and switch into the temp context. */
428         MemoryContextReset(evalContext);
429         oldContext = MemoryContextSwitchTo(evalContext);
430
431         /*
432          * We cannot report a match without checking all the fields, but we
433          * can report a non-match as soon as we find unequal fields.  So,
434          * start comparing at the last field (least significant sort key).
435          * That's the most likely to be different...
436          */
437         result = true;
438
439         for (i = numCols; --i >= 0;)
440         {
441                 AttrNumber      att = matchColIdx[i];
442                 Datum           attr1,
443                                         attr2;
444                 bool            isNull1,
445                                         isNull2;
446
447                 attr1 = heap_getattr(tuple1,
448                                                          att,
449                                                          tupdesc,
450                                                          &isNull1);
451
452                 attr2 = heap_getattr(tuple2,
453                                                          att,
454                                                          tupdesc,
455                                                          &isNull2);
456
457                 if (isNull1 != isNull2)
458                 {
459                         result = false;         /* one null and one not; they aren't equal */
460                         break;
461                 }
462
463                 if (isNull1)
464                         continue;                       /* both are null, treat as equal */
465
466                 /* Apply the type-specific equality function */
467
468                 if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
469                                                                                  attr1, attr2)))
470                 {
471                         result = false;         /* they aren't equal */
472                         break;
473                 }
474         }
475
476         MemoryContextSwitchTo(oldContext);
477
478         return result;
479 }
480
481 /*
482  * execTuplesMatchPrepare
483  *              Look up the equality functions needed for execTuplesMatch.
484  *              The result is a palloc'd array.
485  */
486 FmgrInfo   *
487 execTuplesMatchPrepare(TupleDesc tupdesc,
488                                            int numCols,
489                                            AttrNumber *matchColIdx)
490 {
491         FmgrInfo   *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
492         int                     i;
493
494         for (i = 0; i < numCols; i++)
495         {
496                 AttrNumber      att = matchColIdx[i];
497                 Oid                     typid = tupdesc->attrs[att - 1]->atttypid;
498                 Operator        eq_operator;
499                 Form_pg_operator pgopform;
500
501                 eq_operator = oper("=", typid, typid, true);
502                 if (!HeapTupleIsValid(eq_operator))
503                         elog(ERROR, "Unable to identify an equality operator for type '%s'",
504                                  typeidTypeName(typid));
505                 pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
506                 fmgr_info(pgopform->oprcode, &eqfunctions[i]);
507                 ReleaseSysCache(eq_operator);
508         }
509
510         return eqfunctions;
511 }