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.29 1999/07/17 20:16:58 momjian 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 ExecStoreTuple(grpstate->grp_firstTuple,
95 grpstate->csstate.css_ScanTupleSlot,
101 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
102 if (TupIsNull(outerslot))
104 grpstate->grp_done = TRUE;
107 outerTuple = outerslot->val;
109 firsttuple = grpstate->grp_firstTuple;
110 /* this should occur on the first call only */
111 if (firsttuple == NULL)
112 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
117 * Compare with first tuple and see if this tuple is of the
120 if (!sameGroup(firsttuple, outerTuple,
121 node->numCols, node->grpColIdx,
122 ExecGetScanType(&grpstate->csstate)))
124 grpstate->grp_useFirstTuple = TRUE;
126 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
128 return NULL; /* signifies the end of the group */
132 ExecStoreTuple(outerTuple,
133 grpstate->csstate.css_ScanTupleSlot,
134 outerslot->ttc_buffer,
139 * form a projection tuple, store it in the result tuple
140 * slot and return it.
143 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
145 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
146 resultSlot = ExecProject(projInfo, &isDone);
152 * ExecGroupOneTuple -
153 * returns one tuple per group, a NULL at the end when there are no more
156 static TupleTableSlot *
157 ExecGroupOneTuple(Group *node)
159 GroupState *grpstate;
161 ExprContext *econtext;
163 HeapTuple outerTuple = NULL;
164 HeapTuple firsttuple;
165 TupleTableSlot *outerslot;
166 ProjectionInfo *projInfo;
167 TupleTableSlot *resultSlot;
171 /* ---------------------
172 * get state info from node
173 * ---------------------
175 grpstate = node->grpstate;
176 if (grpstate->grp_done)
179 estate = node->plan.state;
181 econtext = node->grpstate->csstate.cstate.cs_ExprContext;
183 firsttuple = grpstate->grp_firstTuple;
184 /* this should occur on the first call only */
185 if (firsttuple == NULL)
187 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
188 if (TupIsNull(outerslot))
190 grpstate->grp_done = TRUE;
193 grpstate->grp_firstTuple = firsttuple =
194 heap_copytuple(outerslot->val);
198 * find all tuples that belong to a group
202 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
203 if (TupIsNull(outerslot))
205 grpstate->grp_done = TRUE;
209 outerTuple = outerslot->val;
212 * Compare with first tuple and see if this tuple is of
216 if ((!sameGroup(firsttuple, outerTuple,
217 node->numCols, node->grpColIdx,
218 ExecGetScanType(&grpstate->csstate))))
223 * form a projection tuple, store it in the result tuple
224 * slot and return it.
227 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
229 ExecStoreTuple(firsttuple,
230 grpstate->csstate.css_ScanTupleSlot,
233 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
234 resultSlot = ExecProject(projInfo, &isDone);
236 /* save outerTuple if we are not done yet */
237 if (!grpstate->grp_done)
240 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
249 * Creates the run-time information for the group node produced by the
250 * planner and initializes its outer subtree
254 ExecInitGroup(Group *node, EState *estate, Plan *parent)
256 GroupState *grpstate;
260 * assign the node's execution state
262 node->plan.state = estate;
265 * create state structure
267 grpstate = makeNode(GroupState);
268 node->grpstate = grpstate;
269 grpstate->grp_useFirstTuple = FALSE;
270 grpstate->grp_done = FALSE;
271 grpstate->grp_firstTuple = NULL;
274 * assign node's base id and create expression context
276 ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
278 ExecAssignExprContext(estate, &grpstate->csstate.cstate);
280 #define GROUP_NSLOTS 2
283 * tuple table initialization
285 ExecInitScanTupleSlot(estate, &grpstate->csstate);
286 ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
289 * initializes child nodes
291 outerPlan = outerPlan(node);
292 ExecInitNode(outerPlan, estate, (Plan *) node);
295 * initialize tuple type.
298 ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
301 * Initialize tuple type for both result and scan. This node does no
304 ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
305 ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
311 ExecCountSlotsGroup(Group *node)
313 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
316 /* ------------------------
319 * -----------------------
322 ExecEndGroup(Group *node)
324 GroupState *grpstate;
327 grpstate = node->grpstate;
329 ExecFreeProjectionInfo(&grpstate->csstate.cstate);
331 outerPlan = outerPlan(node);
332 ExecEndNode(outerPlan, (Plan *) node);
334 /* clean up tuple table */
335 ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
336 if (grpstate->grp_firstTuple != NULL)
338 pfree(grpstate->grp_firstTuple);
339 grpstate->grp_firstTuple = NULL;
343 /*****************************************************************************
345 *****************************************************************************/
348 * code swiped from nodeUnique.c
351 sameGroup(HeapTuple oldtuple,
354 AttrNumber *grpColIdx,
368 for (i = 0; i < numCols; i++)
371 getTypeOutAndElem((Oid) tupdesc->attrs[att - 1]->atttypid,
372 &typoutput, &typelem);
374 attr1 = heap_getattr(oldtuple,
379 attr2 = heap_getattr(newtuple,
384 if (isNull1 == isNull2)
386 if (isNull1) /* both are null, they are equal */
389 val1 = fmgr(typoutput, attr1, typelem,
390 tupdesc->attrs[att - 1]->atttypmod);
391 val2 = fmgr(typoutput, attr2, typelem,
392 tupdesc->attrs[att - 1]->atttypmod);
395 * now, val1 and val2 are ascii representations so we can use
396 * strcmp for comparison
398 if (strcmp(val1, val2) != 0)
409 /* one is null and the other isn't, they aren't equal */
418 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
420 GroupState *grpstate = node->grpstate;
422 grpstate->grp_useFirstTuple = FALSE;
423 grpstate->grp_done = FALSE;
424 if (grpstate->grp_firstTuple != NULL)
426 pfree(grpstate->grp_firstTuple);
427 grpstate->grp_firstTuple = NULL;
430 if (((Plan *) node)->lefttree &&
431 ((Plan *) node)->lefttree->chgParam == NULL)
432 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);