1 /*-------------------------------------------------------------------------
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7 * Portions Copyright (c) 1994, Regents of the University of California
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.
18 * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.38 2000/08/24 03:29:03 tgl Exp $
20 *-------------------------------------------------------------------------
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"
32 static TupleTableSlot *ExecGroupEveryTuple(Group *node);
33 static TupleTableSlot *ExecGroupOneTuple(Group *node);
35 /* ---------------------------------------
38 * There are two modes in which tuples are returned by ExecGroup. If
39 * tuplePerGroup is TRUE, every tuple from the same group will be
40 * returned, followed by a NULL at the end of each group. This is
41 * useful for Agg node which needs to aggregate over tuples of the same
42 * group. (eg. SELECT salary, count(*) FROM emp GROUP BY salary)
44 * If tuplePerGroup is FALSE, only one tuple per group is returned. The
45 * tuple returned contains only the group columns. NULL is returned only
46 * at the end when no more groups are present. This is useful when
47 * the query does not involve aggregates. (eg. SELECT salary FROM emp
49 * ------------------------------------------
52 ExecGroup(Group *node)
54 if (node->tuplePerGroup)
55 return ExecGroupEveryTuple(node);
57 return ExecGroupOneTuple(node);
61 * ExecGroupEveryTuple -
62 * return every tuple with a NULL between each group
64 static TupleTableSlot *
65 ExecGroupEveryTuple(Group *node)
69 ExprContext *econtext;
71 HeapTuple outerTuple = NULL;
73 TupleTableSlot *outerslot;
74 ProjectionInfo *projInfo;
75 TupleTableSlot *resultSlot;
77 /* ---------------------
78 * get state info from node
79 * ---------------------
81 grpstate = node->grpstate;
82 if (grpstate->grp_done)
84 estate = node->plan.state;
85 econtext = grpstate->csstate.cstate.cs_ExprContext;
86 tupdesc = ExecGetScanType(&grpstate->csstate);
89 * We need not call ResetExprContext here because execTuplesMatch
90 * will reset the per-tuple memory context once per input tuple.
93 /* if we haven't returned first tuple of a new group yet ... */
94 if (grpstate->grp_useFirstTuple)
96 grpstate->grp_useFirstTuple = FALSE;
99 * note we rely on subplan to hold ownership of the tuple for as
100 * long as we need it; we don't copy it.
102 ExecStoreTuple(grpstate->grp_firstTuple,
103 grpstate->csstate.css_ScanTupleSlot,
104 InvalidBuffer, false);
108 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
109 if (TupIsNull(outerslot))
111 grpstate->grp_done = TRUE;
114 outerTuple = outerslot->val;
116 firsttuple = grpstate->grp_firstTuple;
117 if (firsttuple == NULL)
119 /* this should occur on the first call only */
120 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
126 * Compare with first tuple and see if this tuple is of the
129 if (!execTuplesMatch(firsttuple, outerTuple,
131 node->numCols, node->grpColIdx,
132 grpstate->eqfunctions,
133 econtext->ecxt_per_tuple_memory))
137 * No; save the tuple to return it next time, and return
140 grpstate->grp_useFirstTuple = TRUE;
141 heap_freetuple(firsttuple);
142 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
144 return NULL; /* signifies the end of the group */
149 * note we rely on subplan to hold ownership of the tuple for as
150 * long as we need it; we don't copy it.
152 ExecStoreTuple(outerTuple,
153 grpstate->csstate.css_ScanTupleSlot,
154 InvalidBuffer, false);
158 * form a projection tuple, store it in the result tuple
159 * slot and return it.
162 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
164 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
165 resultSlot = ExecProject(projInfo, NULL);
171 * ExecGroupOneTuple -
172 * returns one tuple per group, a NULL at the end when there are no more
175 static TupleTableSlot *
176 ExecGroupOneTuple(Group *node)
178 GroupState *grpstate;
180 ExprContext *econtext;
182 HeapTuple outerTuple = NULL;
183 HeapTuple firsttuple;
184 TupleTableSlot *outerslot;
185 ProjectionInfo *projInfo;
186 TupleTableSlot *resultSlot;
188 /* ---------------------
189 * get state info from node
190 * ---------------------
192 grpstate = node->grpstate;
193 if (grpstate->grp_done)
195 estate = node->plan.state;
196 econtext = node->grpstate->csstate.cstate.cs_ExprContext;
197 tupdesc = ExecGetScanType(&grpstate->csstate);
200 * We need not call ResetExprContext here because execTuplesMatch
201 * will reset the per-tuple memory context once per input tuple.
204 firsttuple = grpstate->grp_firstTuple;
205 if (firsttuple == NULL)
207 /* this should occur on the first call only */
208 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
209 if (TupIsNull(outerslot))
211 grpstate->grp_done = TRUE;
214 grpstate->grp_firstTuple = firsttuple =
215 heap_copytuple(outerslot->val);
219 * find all tuples that belong to a group
223 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
224 if (TupIsNull(outerslot))
226 grpstate->grp_done = TRUE;
230 outerTuple = outerslot->val;
233 * Compare with first tuple and see if this tuple is of the same
236 if (!execTuplesMatch(firsttuple, outerTuple,
238 node->numCols, node->grpColIdx,
239 grpstate->eqfunctions,
240 econtext->ecxt_per_tuple_memory))
245 * form a projection tuple, store it in the result tuple
246 * slot and return it.
249 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
252 * note we rely on subplan to hold ownership of the tuple for as long
253 * as we need it; we don't copy it.
255 ExecStoreTuple(firsttuple,
256 grpstate->csstate.css_ScanTupleSlot,
257 InvalidBuffer, false);
258 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
259 resultSlot = ExecProject(projInfo, NULL);
261 /* save outerTuple if we are not done yet */
262 if (!grpstate->grp_done)
264 heap_freetuple(firsttuple);
265 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
274 * Creates the run-time information for the group node produced by the
275 * planner and initializes its outer subtree
279 ExecInitGroup(Group *node, EState *estate, Plan *parent)
281 GroupState *grpstate;
285 * assign the node's execution state
287 node->plan.state = estate;
290 * create state structure
292 grpstate = makeNode(GroupState);
293 node->grpstate = grpstate;
294 grpstate->grp_useFirstTuple = FALSE;
295 grpstate->grp_done = FALSE;
296 grpstate->grp_firstTuple = NULL;
299 * create expression context
301 ExecAssignExprContext(estate, &grpstate->csstate.cstate);
303 #define GROUP_NSLOTS 2
306 * tuple table initialization
308 ExecInitScanTupleSlot(estate, &grpstate->csstate);
309 ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
312 * initializes child nodes
314 outerPlan = outerPlan(node);
315 ExecInitNode(outerPlan, estate, (Plan *) node);
318 * initialize tuple type.
321 ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
324 * Initialize tuple type for both result and scan. This node does no
327 ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
328 ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
331 * Precompute fmgr lookup data for inner loop
333 grpstate->eqfunctions =
334 execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
342 ExecCountSlotsGroup(Group *node)
344 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
347 /* ------------------------
350 * -----------------------
353 ExecEndGroup(Group *node)
355 GroupState *grpstate;
358 grpstate = node->grpstate;
360 ExecFreeProjectionInfo(&grpstate->csstate.cstate);
361 ExecFreeExprContext(&grpstate->csstate.cstate);
363 outerPlan = outerPlan(node);
364 ExecEndNode(outerPlan, (Plan *) node);
366 /* clean up tuple table */
367 ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
368 if (grpstate->grp_firstTuple != NULL)
370 heap_freetuple(grpstate->grp_firstTuple);
371 grpstate->grp_firstTuple = NULL;
376 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
378 GroupState *grpstate = node->grpstate;
380 grpstate->grp_useFirstTuple = FALSE;
381 grpstate->grp_done = FALSE;
382 if (grpstate->grp_firstTuple != NULL)
384 heap_freetuple(grpstate->grp_firstTuple);
385 grpstate->grp_firstTuple = NULL;
388 if (((Plan *) node)->lefttree &&
389 ((Plan *) node)->lefttree->chgParam == NULL)
390 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
393 /*****************************************************************************
394 * Code shared with nodeUnique.c
395 *****************************************************************************/
399 * Return true if two tuples match in all the indicated fields.
400 * This is used to detect group boundaries in nodeGroup, and to
401 * decide whether two tuples are distinct or not in nodeUnique.
403 * tuple1, tuple2: the tuples to compare
404 * tupdesc: tuple descriptor applying to both tuples
405 * numCols: the number of attributes to be examined
406 * matchColIdx: array of attribute column numbers
407 * eqFunctions: array of fmgr lookup info for the equality functions to use
408 * evalContext: short-term memory context for executing the functions
410 * NB: evalContext is reset each time!
413 execTuplesMatch(HeapTuple tuple1,
417 AttrNumber *matchColIdx,
418 FmgrInfo *eqfunctions,
419 MemoryContext evalContext)
421 MemoryContext oldContext;
425 /* Reset and switch into the temp context. */
426 MemoryContextReset(evalContext);
427 oldContext = MemoryContextSwitchTo(evalContext);
430 * We cannot report a match without checking all the fields, but we
431 * can report a non-match as soon as we find unequal fields. So,
432 * start comparing at the last field (least significant sort key).
433 * That's the most likely to be different...
437 for (i = numCols; --i >= 0;)
439 AttrNumber att = matchColIdx[i];
445 attr1 = heap_getattr(tuple1,
450 attr2 = heap_getattr(tuple2,
455 if (isNull1 != isNull2)
457 result = false; /* one null and one not; they aren't equal */
462 continue; /* both are null, treat as equal */
464 /* Apply the type-specific equality function */
466 if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
469 result = false; /* they aren't equal */
474 MemoryContextSwitchTo(oldContext);
480 * execTuplesMatchPrepare
481 * Look up the equality functions needed for execTuplesMatch.
482 * The result is a palloc'd array.
485 execTuplesMatchPrepare(TupleDesc tupdesc,
487 AttrNumber *matchColIdx)
489 FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
492 for (i = 0; i < numCols; i++)
494 AttrNumber att = matchColIdx[i];
495 Oid typid = tupdesc->attrs[att - 1]->atttypid;
496 Operator eq_operator;
497 Form_pg_operator pgopform;
499 eq_operator = oper("=", typid, typid, true);
500 if (!HeapTupleIsValid(eq_operator))
502 elog(ERROR, "Unable to identify an equality operator for type '%s'",
503 typeidTypeName(typid));
505 pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
506 fmgr_info(pgopform->oprcode, &eqfunctions[i]);