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.22 1998/09/01 04:28:28 momjian Exp $
18 *-------------------------------------------------------------------------
25 #include "access/heapam.h"
26 #include "catalog/catalog.h"
27 #include "access/printtup.h"
28 #include "executor/executor.h"
29 #include "executor/nodeGroup.h"
31 static TupleTableSlot *ExecGroupEveryTuple(Group *node);
32 static TupleTableSlot *ExecGroupOneTuple(Group *node);
33 static bool sameGroup(HeapTuple oldslot, HeapTuple newslot,
34 int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
36 /* ---------------------------------------
39 * There are two modes in which tuples are returned by ExecGroup. If
40 * tuplePerGroup is TRUE, every tuple from the same group will be
41 * returned, followed by a NULL at the end of each group. This is
42 * useful for Agg node which needs to aggregate over tuples of the same
43 * group. (eg. SELECT salary, count{*} FROM emp GROUP BY salary)
45 * If tuplePerGroup is FALSE, only one tuple per group is returned. The
46 * tuple returned contains only the group columns. NULL is returned only
47 * at the end when no more groups is present. This is useful when
48 * the query does not involve aggregates. (eg. SELECT salary FROM emp
50 * ------------------------------------------
53 ExecGroup(Group *node)
55 if (node->tuplePerGroup)
56 return ExecGroupEveryTuple(node);
58 return ExecGroupOneTuple(node);
62 * ExecGroupEveryTuple -
63 * return every tuple with a NULL between each group
65 static TupleTableSlot *
66 ExecGroupEveryTuple(Group *node)
70 ExprContext *econtext;
72 HeapTuple outerTuple = NULL;
74 TupleTableSlot *outerslot;
75 ProjectionInfo *projInfo;
76 TupleTableSlot *resultSlot;
80 /* ---------------------
81 * get state info from node
82 * ---------------------
84 grpstate = node->grpstate;
85 if (grpstate->grp_done)
88 estate = node->plan.state;
90 econtext = grpstate->csstate.cstate.cs_ExprContext;
92 /* if we haven't returned first tuple of new group yet ... */
93 if (grpstate->grp_useFirstTuple)
95 grpstate->grp_useFirstTuple = FALSE;
97 ExecStoreTuple(grpstate->grp_firstTuple,
98 grpstate->csstate.css_ScanTupleSlot,
104 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
106 outerTuple = outerslot->val;
107 if (!HeapTupleIsValid(outerTuple))
109 grpstate->grp_done = TRUE;
113 firsttuple = grpstate->grp_firstTuple;
114 /* this should occur on the first call only */
115 if (firsttuple == NULL)
116 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
121 * Compare with first tuple and see if this tuple is of the
124 if (!sameGroup(firsttuple, outerslot->val,
125 node->numCols, node->grpColIdx,
126 ExecGetScanType(&grpstate->csstate)))
128 grpstate->grp_useFirstTuple = TRUE;
130 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
132 return NULL; /* signifies the end of the group */
136 ExecStoreTuple(outerTuple,
137 grpstate->csstate.css_ScanTupleSlot,
138 outerslot->ttc_buffer,
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);
193 outerTuple = outerslot->val;
194 if (!HeapTupleIsValid(outerTuple))
196 grpstate->grp_done = TRUE;
199 grpstate->grp_firstTuple = firsttuple = heap_copytuple(outerTuple);
203 * find all tuples that belong to a group
207 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
208 outerTuple = (outerslot) ? outerslot->val : NULL;
209 if (!HeapTupleIsValid(outerTuple))
211 grpstate->grp_done = TRUE;
216 * Compare with first tuple and see if this tuple is of
220 if ((!sameGroup(firsttuple, outerslot->val,
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 ExecStoreTuple(firsttuple,
234 grpstate->csstate.css_ScanTupleSlot,
237 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
238 resultSlot = ExecProject(projInfo, &isDone);
240 /* save outerTuple if we are not done yet */
241 if (!grpstate->grp_done)
244 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
253 * Creates the run-time information for the group node produced by the
254 * planner and initializes its outer subtree
258 ExecInitGroup(Group *node, EState *estate, Plan *parent)
260 GroupState *grpstate;
264 * assign the node's execution state
266 node->plan.state = estate;
269 * create state structure
271 grpstate = makeNode(GroupState);
272 node->grpstate = grpstate;
273 grpstate->grp_useFirstTuple = FALSE;
274 grpstate->grp_done = FALSE;
277 * assign node's base id and create expression context
279 ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
281 ExecAssignExprContext(estate, &grpstate->csstate.cstate);
283 #define GROUP_NSLOTS 2
286 * tuple table initialization
288 ExecInitScanTupleSlot(estate, &grpstate->csstate);
289 ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
292 * initializes child nodes
294 outerPlan = outerPlan(node);
295 ExecInitNode(outerPlan, estate, (Plan *) node);
298 * initialize tuple type.
301 ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
304 * Initialize tuple type for both result and scan. This node does no
307 ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
308 ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
314 ExecCountSlotsGroup(Group *node)
316 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
319 /* ------------------------
322 * -----------------------
325 ExecEndGroup(Group *node)
327 GroupState *grpstate;
330 grpstate = node->grpstate;
332 ExecFreeProjectionInfo(&grpstate->csstate.cstate);
334 outerPlan = outerPlan(node);
335 ExecEndNode(outerPlan, (Plan *) node);
337 /* clean up tuple table */
338 ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
339 if (grpstate->grp_firstTuple != NULL)
341 pfree(grpstate->grp_firstTuple);
342 grpstate->grp_firstTuple = NULL;
346 /*****************************************************************************
348 *****************************************************************************/
351 * code swiped from nodeUnique.c
354 sameGroup(HeapTuple oldtuple,
357 AttrNumber *grpColIdx,
370 for (i = 0; i < numCols; i++)
373 typoutput = typtoout((Oid) tupdesc->attrs[att - 1]->atttypid);
375 attr1 = heap_getattr(oldtuple,
380 attr2 = heap_getattr(newtuple,
385 if (isNull1 == isNull2)
387 if (isNull1) /* both are null, they are equal */
390 val1 = fmgr(typoutput, attr1,
391 gettypelem(tupdesc->attrs[att - 1]->atttypid),
392 tupdesc->attrs[att - 1]->atttypmod);
393 val2 = fmgr(typoutput, attr2,
394 gettypelem(tupdesc->attrs[att - 1]->atttypid),
395 tupdesc->attrs[att - 1]->atttypmod);
398 * now, val1 and val2 are ascii representations so we can use
399 * strcmp for comparison
401 if (strcmp(val1, val2) != 0)
412 /* one is null and the other isn't, they aren't equal */
421 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
423 GroupState *grpstate = node->grpstate;
425 grpstate->grp_useFirstTuple = FALSE;
426 grpstate->grp_done = FALSE;
428 if (((Plan *) node)->lefttree &&
429 ((Plan *) node)->lefttree->chgParam == NULL)
430 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);