1 /*-------------------------------------------------------------------------
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Portions Copyright (c) 1996-2002, 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.48 2002/11/06 00:00:43 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 "utils/builtins.h"
31 #include "utils/lsyscache.h"
32 #include "utils/syscache.h"
38 * Return one tuple for each group of matching input tuples.
41 ExecGroup(Group *node)
45 ExprContext *econtext;
47 HeapTuple outerTuple = NULL;
49 TupleTableSlot *outerslot;
50 ProjectionInfo *projInfo;
51 TupleTableSlot *resultSlot;
54 * get state info from node
56 grpstate = node->grpstate;
57 if (grpstate->grp_done)
59 estate = node->plan.state;
60 econtext = node->grpstate->csstate.cstate.cs_ExprContext;
61 tupdesc = ExecGetScanType(&grpstate->csstate);
64 * We need not call ResetExprContext here because execTuplesMatch will
65 * reset the per-tuple memory context once per input tuple.
68 /* If we don't already have first tuple of group, fetch it */
69 /* this should occur on the first call only */
70 firsttuple = grpstate->grp_firstTuple;
71 if (firsttuple == NULL)
73 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
74 if (TupIsNull(outerslot))
76 grpstate->grp_done = TRUE;
79 grpstate->grp_firstTuple = firsttuple =
80 heap_copytuple(outerslot->val);
84 * Scan over all tuples that belong to this group
88 outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
89 if (TupIsNull(outerslot))
91 grpstate->grp_done = TRUE;
95 outerTuple = outerslot->val;
98 * Compare with first tuple and see if this tuple is of the same
101 if (!execTuplesMatch(firsttuple, outerTuple,
103 node->numCols, node->grpColIdx,
104 grpstate->eqfunctions,
105 econtext->ecxt_per_tuple_memory))
110 * form a projection tuple based on the (copied) first tuple of the
111 * group, and store it in the result tuple slot.
113 ExecStoreTuple(firsttuple,
114 grpstate->csstate.css_ScanTupleSlot,
117 econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
118 projInfo = grpstate->csstate.cstate.cs_ProjInfo;
119 resultSlot = ExecProject(projInfo, NULL);
121 /* save first tuple of next group, if we are not done yet */
122 if (!grpstate->grp_done)
124 heap_freetuple(firsttuple);
125 grpstate->grp_firstTuple = heap_copytuple(outerTuple);
134 * Creates the run-time information for the group node produced by the
135 * planner and initializes its outer subtree
139 ExecInitGroup(Group *node, EState *estate, Plan *parent)
141 GroupState *grpstate;
145 * assign the node's execution state
147 node->plan.state = estate;
150 * create state structure
152 grpstate = makeNode(GroupState);
153 node->grpstate = grpstate;
154 grpstate->grp_useFirstTuple = FALSE;
155 grpstate->grp_done = FALSE;
156 grpstate->grp_firstTuple = NULL;
159 * create expression context
161 ExecAssignExprContext(estate, &grpstate->csstate.cstate);
163 #define GROUP_NSLOTS 2
166 * tuple table initialization
168 ExecInitScanTupleSlot(estate, &grpstate->csstate);
169 ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
172 * initializes child nodes
174 outerPlan = outerPlan(node);
175 ExecInitNode(outerPlan, estate, (Plan *) node);
178 * initialize tuple type.
180 ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
183 * Initialize tuple type for both result and scan. This node does no
186 ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
187 ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
190 * Precompute fmgr lookup data for inner loop
192 grpstate->eqfunctions =
193 execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
201 ExecCountSlotsGroup(Group *node)
203 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
206 /* ------------------------
209 * -----------------------
212 ExecEndGroup(Group *node)
214 GroupState *grpstate;
217 grpstate = node->grpstate;
219 ExecFreeProjectionInfo(&grpstate->csstate.cstate);
220 ExecFreeExprContext(&grpstate->csstate.cstate);
222 outerPlan = outerPlan(node);
223 ExecEndNode(outerPlan, (Plan *) node);
225 /* clean up tuple table */
226 ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
227 if (grpstate->grp_firstTuple != NULL)
229 heap_freetuple(grpstate->grp_firstTuple);
230 grpstate->grp_firstTuple = NULL;
235 ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
237 GroupState *grpstate = node->grpstate;
239 grpstate->grp_useFirstTuple = FALSE;
240 grpstate->grp_done = FALSE;
241 if (grpstate->grp_firstTuple != NULL)
243 heap_freetuple(grpstate->grp_firstTuple);
244 grpstate->grp_firstTuple = NULL;
247 if (((Plan *) node)->lefttree &&
248 ((Plan *) node)->lefttree->chgParam == NULL)
249 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
252 /*****************************************************************************
253 * Code shared with nodeUnique.c and nodeAgg.c
254 *****************************************************************************/
258 * Return true if two tuples match in all the indicated fields.
259 * This is used to detect group boundaries in nodeGroup and nodeAgg,
260 * and to decide whether two tuples are distinct or not in nodeUnique.
262 * tuple1, tuple2: the tuples to compare
263 * tupdesc: tuple descriptor applying to both tuples
264 * numCols: the number of attributes to be examined
265 * matchColIdx: array of attribute column numbers
266 * eqFunctions: array of fmgr lookup info for the equality functions to use
267 * evalContext: short-term memory context for executing the functions
269 * NB: evalContext is reset each time!
272 execTuplesMatch(HeapTuple tuple1,
276 AttrNumber *matchColIdx,
277 FmgrInfo *eqfunctions,
278 MemoryContext evalContext)
280 MemoryContext oldContext;
284 /* Reset and switch into the temp context. */
285 MemoryContextReset(evalContext);
286 oldContext = MemoryContextSwitchTo(evalContext);
289 * We cannot report a match without checking all the fields, but we
290 * can report a non-match as soon as we find unequal fields. So,
291 * start comparing at the last field (least significant sort key).
292 * That's the most likely to be different if we are dealing with
297 for (i = numCols; --i >= 0;)
299 AttrNumber att = matchColIdx[i];
305 attr1 = heap_getattr(tuple1,
310 attr2 = heap_getattr(tuple2,
315 if (isNull1 != isNull2)
317 result = false; /* one null and one not; they aren't equal */
322 continue; /* both are null, treat as equal */
324 /* Apply the type-specific equality function */
326 if (!DatumGetBool(FunctionCall2(&eqfunctions[i],
329 result = false; /* they aren't equal */
334 MemoryContextSwitchTo(oldContext);
340 * execTuplesMatchPrepare
341 * Look up the equality functions needed for execTuplesMatch.
342 * The result is a palloc'd array.
345 execTuplesMatchPrepare(TupleDesc tupdesc,
347 AttrNumber *matchColIdx)
349 FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
352 for (i = 0; i < numCols; i++)
354 AttrNumber att = matchColIdx[i];
355 Oid typid = tupdesc->attrs[att - 1]->atttypid;
358 eq_function = compatible_oper_funcid(makeList1(makeString("=")),
360 if (!OidIsValid(eq_function))
361 elog(ERROR, "Unable to identify an equality operator for type %s",
362 format_type_be(typid));
363 fmgr_info(eq_function, &eqfunctions[i]);