1 /*-------------------------------------------------------------------------
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
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.40 2001/01/24 19:42:54 momjian 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"
31 #include "utils/lsyscache.h"
32 #include "utils/syscache.h"
34 static TupleTableSlot *ExecGroupEveryTuple(Group *node);
35 static TupleTableSlot *ExecGroupOneTuple(Group *node);
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 are 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;
75 TupleTableSlot *outerslot;
76 ProjectionInfo *projInfo;
77 TupleTableSlot *resultSlot;
79 /* ---------------------
80 * get state info from node
81 * ---------------------
83 grpstate = node->grpstate;
84 if (grpstate->grp_done)
86 estate = node->plan.state;
87 econtext = grpstate->csstate.cstate.cs_ExprContext;
88 tupdesc = ExecGetScanType(&grpstate->csstate);
91 * We need not call ResetExprContext here because execTuplesMatch
92 * will reset the per-tuple memory context once per input tuple.
95 /* if we haven't returned first tuple of a new group yet ... */
96 if (grpstate->grp_useFirstTuple)
98 grpstate->grp_useFirstTuple = FALSE;
101 * note we rely on subplan to hold ownership of the tuple for as
102 * long as we need it; we don't copy it.
104 ExecStoreTuple(grpstate->grp_firstTuple,
105 grpstate->csstate.css_ScanTupleSlot,
106 InvalidBuffer, false);
110 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
111 if (TupIsNull(outerslot))
113 grpstate->grp_done = TRUE;
116 outerTuple = outerslot->val;
118 firsttuple = grpstate->grp_firstTuple;
119 if (firsttuple == NULL)
121 /* this should occur on the first call only */
122 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
128 * Compare with first tuple and see if this tuple is of the
131 if (!execTuplesMatch(firsttuple, outerTuple,
133 node->numCols, node->grpColIdx,
134 grpstate->eqfunctions,
135 econtext->ecxt_per_tuple_memory))
139 * No; save the tuple to return it next time, and return
142 grpstate->grp_useFirstTuple = TRUE;
143 heap_freetuple(firsttuple);
144 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
146 return NULL; /* signifies the end of the group */
151 * note we rely on subplan to hold ownership of the tuple for as
152 * long as we need it; we don't copy it.
154 ExecStoreTuple(outerTuple,
155 grpstate->csstate.css_ScanTupleSlot,
156 InvalidBuffer, false);
160 * form a projection tuple, store it in the result tuple
161 * slot and return it.
164 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
166 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
167 resultSlot = ExecProject(projInfo, NULL);
173 * ExecGroupOneTuple -
174 * returns one tuple per group, a NULL at the end when there are no more
177 static TupleTableSlot *
178 ExecGroupOneTuple(Group *node)
180 GroupState *grpstate;
182 ExprContext *econtext;
184 HeapTuple outerTuple = NULL;
185 HeapTuple firsttuple;
186 TupleTableSlot *outerslot;
187 ProjectionInfo *projInfo;
188 TupleTableSlot *resultSlot;
190 /* ---------------------
191 * get state info from node
192 * ---------------------
194 grpstate = node->grpstate;
195 if (grpstate->grp_done)
197 estate = node->plan.state;
198 econtext = node->grpstate->csstate.cstate.cs_ExprContext;
199 tupdesc = ExecGetScanType(&grpstate->csstate);
202 * We need not call ResetExprContext here because execTuplesMatch
203 * will reset the per-tuple memory context once per input tuple.
206 firsttuple = grpstate->grp_firstTuple;
207 if (firsttuple == NULL)
209 /* this should occur on the first call only */
210 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
211 if (TupIsNull(outerslot))
213 grpstate->grp_done = TRUE;
216 grpstate->grp_firstTuple = firsttuple =
217 heap_copytuple(outerslot->val);
221 * find all tuples that belong to a group
225 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
226 if (TupIsNull(outerslot))
228 grpstate->grp_done = TRUE;
232 outerTuple = outerslot->val;
235 * Compare with first tuple and see if this tuple is of the same
238 if (!execTuplesMatch(firsttuple, outerTuple,
240 node->numCols, node->grpColIdx,
241 grpstate->eqfunctions,
242 econtext->ecxt_per_tuple_memory))
247 * form a projection tuple, store it in the result tuple
248 * slot and return it.
251 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
254 * note we rely on subplan to hold ownership of the tuple for as long
255 * as we need it; we don't copy it.
257 ExecStoreTuple(firsttuple,
258 grpstate->csstate.css_ScanTupleSlot,
259 InvalidBuffer, false);
260 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
261 resultSlot = ExecProject(projInfo, NULL);
263 /* save outerTuple if we are not done yet */
264 if (!grpstate->grp_done)
266 heap_freetuple(firsttuple);
267 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
276 * Creates the run-time information for the group node produced by the
277 * planner and initializes its outer subtree
281 ExecInitGroup(Group *node, EState *estate, Plan *parent)
283 GroupState *grpstate;
287 * assign the node's execution state
289 node->plan.state = estate;
292 * create state structure
294 grpstate = makeNode(GroupState);
295 node->grpstate = grpstate;
296 grpstate->grp_useFirstTuple = FALSE;
297 grpstate->grp_done = FALSE;
298 grpstate->grp_firstTuple = NULL;
301 * create expression context
303 ExecAssignExprContext(estate, &grpstate->csstate.cstate);
305 #define GROUP_NSLOTS 2
308 * tuple table initialization
310 ExecInitScanTupleSlot(estate, &grpstate->csstate);
311 ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
314 * initializes child nodes
316 outerPlan = outerPlan(node);
317 ExecInitNode(outerPlan, estate, (Plan *) node);
320 * initialize tuple type.
323 ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
326 * Initialize tuple type for both result and scan. This node does no
329 ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
330 ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
333 * Precompute fmgr lookup data for inner loop
335 grpstate->eqfunctions =
336 execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
344 ExecCountSlotsGroup(Group *node)
346 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
349 /* ------------------------
352 * -----------------------
355 ExecEndGroup(Group *node)
357 GroupState *grpstate;
360 grpstate = node->grpstate;
362 ExecFreeProjectionInfo(&grpstate->csstate.cstate);
363 ExecFreeExprContext(&grpstate->csstate.cstate);
365 outerPlan = outerPlan(node);
366 ExecEndNode(outerPlan, (Plan *) node);
368 /* clean up tuple table */
369 ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
370 if (grpstate->grp_firstTuple != NULL)
372 heap_freetuple(grpstate->grp_firstTuple);
373 grpstate->grp_firstTuple = NULL;
378 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
380 GroupState *grpstate = node->grpstate;
382 grpstate->grp_useFirstTuple = FALSE;
383 grpstate->grp_done = FALSE;
384 if (grpstate->grp_firstTuple != NULL)
386 heap_freetuple(grpstate->grp_firstTuple);
387 grpstate->grp_firstTuple = NULL;
390 if (((Plan *) node)->lefttree &&
391 ((Plan *) node)->lefttree->chgParam == NULL)
392 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
395 /*****************************************************************************
396 * Code shared with nodeUnique.c
397 *****************************************************************************/
401 * Return true if two tuples match in all the indicated fields.
402 * This is used to detect group boundaries in nodeGroup, and to
403 * decide whether two tuples are distinct or not in nodeUnique.
405 * tuple1, tuple2: the tuples to compare
406 * tupdesc: tuple descriptor applying to both tuples
407 * numCols: the number of attributes to be examined
408 * matchColIdx: array of attribute column numbers
409 * eqFunctions: array of fmgr lookup info for the equality functions to use
410 * evalContext: short-term memory context for executing the functions
412 * NB: evalContext is reset each time!
415 execTuplesMatch(HeapTuple tuple1,
419 AttrNumber *matchColIdx,
420 FmgrInfo *eqfunctions,
421 MemoryContext evalContext)
423 MemoryContext oldContext;
427 /* Reset and switch into the temp context. */
428 MemoryContextReset(evalContext);
429 oldContext = MemoryContextSwitchTo(evalContext);
432 * We cannot report a match without checking all the fields, but we
433 * can report a non-match as soon as we find unequal fields. So,
434 * start comparing at the last field (least significant sort key).
435 * That's the most likely to be different...
439 for (i = numCols; --i >= 0;)
441 AttrNumber att = matchColIdx[i];
447 attr1 = heap_getattr(tuple1,
452 attr2 = heap_getattr(tuple2,
457 if (isNull1 != isNull2)
459 result = false; /* one null and one not; they aren't equal */
464 continue; /* both are null, treat as equal */
466 /* Apply the type-specific equality function */
468 if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
471 result = false; /* they aren't equal */
476 MemoryContextSwitchTo(oldContext);
482 * execTuplesMatchPrepare
483 * Look up the equality functions needed for execTuplesMatch.
484 * The result is a palloc'd array.
487 execTuplesMatchPrepare(TupleDesc tupdesc,
489 AttrNumber *matchColIdx)
491 FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
494 for (i = 0; i < numCols; i++)
496 AttrNumber att = matchColIdx[i];
497 Oid typid = tupdesc->attrs[att - 1]->atttypid;
498 Operator eq_operator;
499 Form_pg_operator pgopform;
501 eq_operator = oper("=", typid, typid, true);
502 if (!HeapTupleIsValid(eq_operator))
503 elog(ERROR, "Unable to identify an equality operator for type '%s'",
504 typeidTypeName(typid));
505 pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
506 fmgr_info(pgopform->oprcode, &eqfunctions[i]);
507 ReleaseSysCache(eq_operator);