1 /*-------------------------------------------------------------------------
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Copyright (c) 1994, Regents of the University of California
10 * The Group node is designed for handling queries with a GROUP BY clause.
11 * It's outer plan must be a sort node. It assumes that the tuples it gets
12 * back from the outer plan is sorted in the order specified by the group
13 * columns. (ie. tuples from the same group are consecutive)
16 * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.30 1999/09/24 00:24:23 tgl Exp $
18 *-------------------------------------------------------------------------
23 #include "access/heapam.h"
24 #include "access/printtup.h"
25 #include "executor/executor.h"
26 #include "executor/nodeGroup.h"
28 static TupleTableSlot *ExecGroupEveryTuple(Group *node);
29 static TupleTableSlot *ExecGroupOneTuple(Group *node);
30 static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
31 int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
33 /* ---------------------------------------
36 * There are two modes in which tuples are returned by ExecGroup. If
37 * tuplePerGroup is TRUE, every tuple from the same group will be
38 * returned, followed by a NULL at the end of each group. This is
39 * useful for Agg node which needs to aggregate over tuples of the same
40 * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
42 * If tuplePerGroup is FALSE, only one tuple per group is returned. The
43 * tuple returned contains only the group columns. NULL is returned only
44 * at the end when no more groups is present. This is useful when
45 * the query does not involve aggregates. (eg. SELECT salary FROM emp
47 * ------------------------------------------
50 ExecGroup(Group *node)
52 if (node->tuplePerGroup)
53 return ExecGroupEveryTuple(node);
55 return ExecGroupOneTuple(node);
59 * ExecGroupEveryTuple -
60 * return every tuple with a NULL between each group
62 static TupleTableSlot *
63 ExecGroupEveryTuple(Group *node)
67 ExprContext *econtext;
69 HeapTuple outerTuple = NULL;
71 TupleTableSlot *outerslot;
72 ProjectionInfo *projInfo;
73 TupleTableSlot *resultSlot;
77 /* ---------------------
78 * get state info from node
79 * ---------------------
81 grpstate = node->grpstate;
82 if (grpstate->grp_done)
85 estate = node->plan.state;
87 econtext = grpstate->csstate.cstate.cs_ExprContext;
89 /* if we haven't returned first tuple of new group yet ... */
90 if (grpstate->grp_useFirstTuple)
92 grpstate->grp_useFirstTuple = FALSE;
94 /* note we rely on subplan to hold ownership of the tuple
95 * for as long as we need it; we don't copy it.
97 ExecStoreTuple(grpstate->grp_firstTuple,
98 grpstate->csstate.css_ScanTupleSlot,
99 InvalidBuffer, false);
103 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
104 if (TupIsNull(outerslot))
106 grpstate->grp_done = TRUE;
109 outerTuple = outerslot->val;
111 firsttuple = grpstate->grp_firstTuple;
112 /* this should occur on the first call only */
113 if (firsttuple == NULL)
114 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
119 * Compare with first tuple and see if this tuple is of the
122 if (!sameGroup(firsttuple, outerTuple,
123 node->numCols, node->grpColIdx,
124 ExecGetScanType(&grpstate->csstate)))
126 grpstate->grp_useFirstTuple = TRUE;
128 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
130 return NULL; /* signifies the end of the group */
134 /* note we rely on subplan to hold ownership of the tuple
135 * for as long as we need it; we don't copy it.
137 ExecStoreTuple(outerTuple,
138 grpstate->csstate.css_ScanTupleSlot,
139 InvalidBuffer, false);
143 * form a projection tuple, store it in the result tuple
144 * slot and return it.
147 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
149 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
150 resultSlot = ExecProject(projInfo, &isDone);
156 * ExecGroupOneTuple -
157 * returns one tuple per group, a NULL at the end when there are no more
160 static TupleTableSlot *
161 ExecGroupOneTuple(Group *node)
163 GroupState *grpstate;
165 ExprContext *econtext;
167 HeapTuple outerTuple = NULL;
168 HeapTuple firsttuple;
169 TupleTableSlot *outerslot;
170 ProjectionInfo *projInfo;
171 TupleTableSlot *resultSlot;
175 /* ---------------------
176 * get state info from node
177 * ---------------------
179 grpstate = node->grpstate;
180 if (grpstate->grp_done)
183 estate = node->plan.state;
185 econtext = node->grpstate->csstate.cstate.cs_ExprContext;
187 firsttuple = grpstate->grp_firstTuple;
188 /* this should occur on the first call only */
189 if (firsttuple == NULL)
191 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
192 if (TupIsNull(outerslot))
194 grpstate->grp_done = TRUE;
197 grpstate->grp_firstTuple = firsttuple =
198 heap_copytuple(outerslot->val);
202 * find all tuples that belong to a group
206 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
207 if (TupIsNull(outerslot))
209 grpstate->grp_done = TRUE;
213 outerTuple = outerslot->val;
216 * Compare with first tuple and see if this tuple is of
220 if ((!sameGroup(firsttuple, outerTuple,
221 node->numCols, node->grpColIdx,
222 ExecGetScanType(&grpstate->csstate))))
227 * form a projection tuple, store it in the result tuple
228 * slot and return it.
231 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
233 /* note we rely on subplan to hold ownership of the tuple
234 * for as long as we need it; we don't copy it.
236 ExecStoreTuple(firsttuple,
237 grpstate->csstate.css_ScanTupleSlot,
238 InvalidBuffer, false);
239 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
240 resultSlot = ExecProject(projInfo, &isDone);
242 /* save outerTuple if we are not done yet */
243 if (!grpstate->grp_done)
246 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
255 * Creates the run-time information for the group node produced by the
256 * planner and initializes its outer subtree
260 ExecInitGroup(Group *node, EState *estate, Plan *parent)
262 GroupState *grpstate;
266 * assign the node's execution state
268 node->plan.state = estate;
271 * create state structure
273 grpstate = makeNode(GroupState);
274 node->grpstate = grpstate;
275 grpstate->grp_useFirstTuple = FALSE;
276 grpstate->grp_done = FALSE;
277 grpstate->grp_firstTuple = NULL;
280 * assign node's base id and create expression context
282 ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
284 ExecAssignExprContext(estate, &grpstate->csstate.cstate);
286 #define GROUP_NSLOTS 2
289 * tuple table initialization
291 ExecInitScanTupleSlot(estate, &grpstate->csstate);
292 ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
295 * initializes child nodes
297 outerPlan = outerPlan(node);
298 ExecInitNode(outerPlan, estate, (Plan *) node);
301 * initialize tuple type.
304 ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
307 * Initialize tuple type for both result and scan. This node does no
310 ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
311 ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
317 ExecCountSlotsGroup(Group *node)
319 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
322 /* ------------------------
325 * -----------------------
328 ExecEndGroup(Group *node)
330 GroupState *grpstate;
333 grpstate = node->grpstate;
335 ExecFreeProjectionInfo(&grpstate->csstate.cstate);
337 outerPlan = outerPlan(node);
338 ExecEndNode(outerPlan, (Plan *) node);
340 /* clean up tuple table */
341 ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
342 if (grpstate->grp_firstTuple != NULL)
344 pfree(grpstate->grp_firstTuple);
345 grpstate->grp_firstTuple = NULL;
349 /*****************************************************************************
351 *****************************************************************************/
354 * code swiped from nodeUnique.c
357 sameGroup(HeapTuple oldtuple,
360 AttrNumber *grpColIdx,
374 for (i = 0; i < numCols; i++)
377 getTypeOutAndElem((Oid) tupdesc->attrs[att - 1]->atttypid,
378 &typoutput, &typelem);
380 attr1 = heap_getattr(oldtuple,
385 attr2 = heap_getattr(newtuple,
390 if (isNull1 == isNull2)
392 if (isNull1) /* both are null, they are equal */
395 val1 = fmgr(typoutput, attr1, typelem,
396 tupdesc->attrs[att - 1]->atttypmod);
397 val2 = fmgr(typoutput, attr2, typelem,
398 tupdesc->attrs[att - 1]->atttypmod);
401 * now, val1 and val2 are ascii representations so we can use
402 * strcmp for comparison
404 if (strcmp(val1, val2) != 0)
415 /* one is null and the other isn't, they aren't equal */
424 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
426 GroupState *grpstate = node->grpstate;
428 grpstate->grp_useFirstTuple = FALSE;
429 grpstate->grp_done = FALSE;
430 if (grpstate->grp_firstTuple != NULL)
432 pfree(grpstate->grp_firstTuple);
433 grpstate->grp_firstTuple = NULL;
436 if (((Plan *) node)->lefttree &&
437 ((Plan *) node)->lefttree->chgParam == NULL)
438 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);