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.13 1998/01/27 15:41:32 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);
34 sameGroup(TupleTableSlot *oldslot, TupleTableSlot *newslot,
35 int numCols, AttrNumber *grpColIdx, TupleDesc tupdesc);
37 /* ---------------------------------------
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)
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 is present. This is useful when
49 * the query does not involve aggregates. (eg. SELECT salary FROM emp
51 * ------------------------------------------
54 ExecGroup(Group *node)
56 if (node->tuplePerGroup)
57 return ExecGroupEveryTuple(node);
59 return ExecGroupOneTuple(node);
63 * ExecGroupEveryTuple -
64 * return every tuple with a NULL between each group
66 static TupleTableSlot *
67 ExecGroupEveryTuple(Group *node)
71 ExprContext *econtext;
73 HeapTuple outerTuple = NULL;
74 TupleTableSlot *outerslot,
76 ProjectionInfo *projInfo;
77 TupleTableSlot *resultSlot;
81 /* ---------------------
82 * get state info from node
83 * ---------------------
85 grpstate = node->grpstate;
86 if (grpstate->grp_done)
89 estate = node->plan.state;
91 econtext = grpstate->csstate.cstate.cs_ExprContext;
93 if (grpstate->grp_useLastTuple)
97 * we haven't returned last tuple yet because it is not of the
100 grpstate->grp_useLastTuple = FALSE;
102 ExecStoreTuple(grpstate->grp_lastSlot->val,
103 grpstate->csstate.css_ScanTupleSlot,
104 grpstate->grp_lastSlot->ttc_buffer,
109 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
111 outerTuple = outerslot->val;
112 if (!HeapTupleIsValid(outerTuple))
114 grpstate->grp_done = TRUE;
119 * Compare with last tuple and see if this tuple is of
123 lastslot = grpstate->csstate.css_ScanTupleSlot;
125 if (lastslot->val != NULL &&
126 (!sameGroup(lastslot, outerslot,
127 node->numCols, node->grpColIdx,
128 ExecGetScanType(&grpstate->csstate))))
130 /* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
132 grpstate->grp_useLastTuple = TRUE;
134 /* save it for next time */
135 grpstate->grp_lastSlot = outerslot;
138 * signifies the end of the group
143 ExecStoreTuple(outerTuple,
144 grpstate->csstate.css_ScanTupleSlot,
145 outerslot->ttc_buffer,
150 * form a projection tuple, store it in the result tuple
151 * slot and return it.
154 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
156 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
157 resultSlot = ExecProject(projInfo, &isDone);
163 * ExecGroupOneTuple -
164 * returns one tuple per group, a NULL at the end when there are no more
167 static TupleTableSlot *
168 ExecGroupOneTuple(Group *node)
170 GroupState *grpstate;
172 ExprContext *econtext;
174 HeapTuple outerTuple = NULL;
175 TupleTableSlot *outerslot,
177 ProjectionInfo *projInfo;
178 TupleTableSlot *resultSlot;
182 /* ---------------------
183 * get state info from node
184 * ---------------------
186 grpstate = node->grpstate;
187 if (grpstate->grp_done)
190 estate = node->plan.state;
192 econtext = node->grpstate->csstate.cstate.cs_ExprContext;
194 if (grpstate->grp_useLastTuple)
196 grpstate->grp_useLastTuple = FALSE;
197 ExecStoreTuple(grpstate->grp_lastSlot->val,
198 grpstate->csstate.css_ScanTupleSlot,
199 grpstate->grp_lastSlot->ttc_buffer,
204 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
206 outerTuple = outerslot->val;
207 if (!HeapTupleIsValid(outerTuple))
209 grpstate->grp_done = TRUE;
212 ExecStoreTuple(outerTuple,
213 grpstate->csstate.css_ScanTupleSlot,
214 outerslot->ttc_buffer,
217 lastslot = grpstate->csstate.css_ScanTupleSlot;
220 * find all tuples that belong to a group
224 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
225 outerTuple = (outerslot) ? outerslot->val : NULL;
226 if (!HeapTupleIsValid(outerTuple))
230 * we have at least one tuple (lastslot) if we reach here
232 grpstate->grp_done = TRUE;
234 /* return lastslot */
239 * Compare with last tuple and see if this tuple is of
243 if ((!sameGroup(lastslot, outerslot,
244 node->numCols, node->grpColIdx,
245 ExecGetScanType(&grpstate->csstate))))
247 /* ExecGetResultType(&grpstate->csstate.cstate)))) {*/
249 grpstate->grp_useLastTuple = TRUE;
251 /* save it for next time */
252 grpstate->grp_lastSlot = outerslot;
254 /* return lastslot */
258 ExecStoreTuple(outerTuple,
259 grpstate->csstate.css_ScanTupleSlot,
260 outerslot->ttc_buffer,
263 lastslot = grpstate->csstate.css_ScanTupleSlot;
266 ExecStoreTuple(lastslot->val,
267 grpstate->csstate.css_ScanTupleSlot,
268 lastslot->ttc_buffer,
272 * form a projection tuple, store it in the result tuple
273 * slot and return it.
276 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
278 econtext->ecxt_scantuple = lastslot;
279 resultSlot = ExecProject(projInfo, &isDone);
287 * Creates the run-time information for the group node produced by the
288 * planner and initializes its outer subtree
292 ExecInitGroup(Group *node, EState *estate, Plan *parent)
294 GroupState *grpstate;
298 * assign the node's execution state
300 node->plan.state = estate;
303 * create state structure
305 grpstate = makeNode(GroupState);
306 node->grpstate = grpstate;
307 grpstate->grp_useLastTuple = FALSE;
308 grpstate->grp_done = FALSE;
311 * assign node's base id and create expression context
313 ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
315 ExecAssignExprContext(estate, &grpstate->csstate.cstate);
317 #define GROUP_NSLOTS 2
320 * tuple table initialization
322 ExecInitScanTupleSlot(estate, &grpstate->csstate);
323 ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
326 * initializes child nodes
328 outerPlan = outerPlan(node);
329 ExecInitNode(outerPlan, estate, (Plan *) node);
332 * initialize tuple type.
335 ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
338 * Initialize tuple type for both result and scan. This node does no
341 ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
342 ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
348 ExecCountSlotsGroup(Group *node)
350 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
353 /* ------------------------
356 * -----------------------
359 ExecEndGroup(Group *node)
361 GroupState *grpstate;
364 grpstate = node->grpstate;
366 ExecFreeProjectionInfo(&grpstate->csstate.cstate);
368 outerPlan = outerPlan(node);
369 ExecEndNode(outerPlan, (Plan *) node);
371 /* clean up tuple table */
372 ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
375 /*****************************************************************************
377 *****************************************************************************/
380 * code swiped from nodeUnique.c
383 sameGroup(TupleTableSlot *oldslot,
384 TupleTableSlot *newslot,
386 AttrNumber *grpColIdx,
399 for (i = 0; i < numCols; i++)
402 typoutput = typtoout((Oid) tupdesc->attrs[att - 1]->atttypid);
404 attr1 = heap_getattr(oldslot->val,
410 attr2 = heap_getattr(newslot->val,
416 if (isNull1 == isNull2)
418 if (isNull1) /* both are null, they are equal */
421 val1 = fmgr(typoutput, attr1,
422 gettypelem(tupdesc->attrs[att - 1]->atttypid));
423 val2 = fmgr(typoutput, attr2,
424 gettypelem(tupdesc->attrs[att - 1]->atttypid));
427 * now, val1 and val2 are ascii representations so we can use
428 * strcmp for comparison
430 if (strcmp(val1, val2) != 0)
441 /* one is null and the other isn't, they aren't equal */