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.51 2002/12/05 15:50:33 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(GroupState *node)
44 ExprContext *econtext;
47 AttrNumber *grpColIdx;
48 HeapTuple outerTuple = NULL;
50 TupleTableSlot *outerslot;
51 ProjectionInfo *projInfo;
52 TupleTableSlot *resultSlot;
55 * get state info from node
59 estate = node->ss.ps.state;
60 econtext = node->ss.ps.ps_ExprContext;
61 tupdesc = ExecGetScanType(&node->ss);
62 numCols = ((Group *) node->ss.ps.plan)->numCols;
63 grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
66 * We need not call ResetExprContext here because execTuplesMatch will
67 * reset the per-tuple memory context once per input tuple.
70 /* If we don't already have first tuple of group, fetch it */
71 /* this should occur on the first call only */
72 firsttuple = node->grp_firstTuple;
73 if (firsttuple == NULL)
75 outerslot = ExecProcNode(outerPlanState(node));
76 if (TupIsNull(outerslot))
78 node->grp_done = TRUE;
81 node->grp_firstTuple = firsttuple =
82 heap_copytuple(outerslot->val);
86 * Scan over all tuples that belong to this group
90 outerslot = ExecProcNode(outerPlanState(node));
91 if (TupIsNull(outerslot))
93 node->grp_done = TRUE;
97 outerTuple = outerslot->val;
100 * Compare with first tuple and see if this tuple is of the same
103 if (!execTuplesMatch(firsttuple, outerTuple,
107 econtext->ecxt_per_tuple_memory))
112 * form a projection tuple based on the (copied) first tuple of the
113 * group, and store it in the result tuple slot.
115 ExecStoreTuple(firsttuple,
116 node->ss.ss_ScanTupleSlot,
119 econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
120 projInfo = node->ss.ps.ps_ProjInfo;
121 resultSlot = ExecProject(projInfo, NULL);
123 /* save first tuple of next group, if we are not done yet */
126 heap_freetuple(firsttuple);
127 node->grp_firstTuple = heap_copytuple(outerTuple);
136 * Creates the run-time information for the group node produced by the
137 * planner and initializes its outer subtree
141 ExecInitGroup(Group *node, EState *estate)
143 GroupState *grpstate;
146 * create state structure
148 grpstate = makeNode(GroupState);
149 grpstate->ss.ps.plan = (Plan *) node;
150 grpstate->ss.ps.state = estate;
151 grpstate->grp_firstTuple = NULL;
152 grpstate->grp_done = FALSE;
155 * create expression context
157 ExecAssignExprContext(estate, &grpstate->ss.ps);
159 #define GROUP_NSLOTS 2
162 * tuple table initialization
164 ExecInitScanTupleSlot(estate, &grpstate->ss);
165 ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
168 * initialize child expressions
170 grpstate->ss.ps.targetlist = (List *)
171 ExecInitExpr((Node *) node->plan.targetlist,
172 (PlanState *) grpstate);
173 grpstate->ss.ps.qual = (List *)
174 ExecInitExpr((Node *) node->plan.qual,
175 (PlanState *) grpstate);
178 * initialize child nodes
180 outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate);
183 * initialize tuple type.
185 ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
188 * Initialize tuple type for both result and scan. This node does no
191 ExecAssignResultTypeFromTL(&grpstate->ss.ps);
192 ExecAssignProjectionInfo(&grpstate->ss.ps);
195 * Precompute fmgr lookup data for inner loop
197 grpstate->eqfunctions =
198 execTuplesMatchPrepare(ExecGetScanType(&grpstate->ss),
206 ExecCountSlotsGroup(Group *node)
208 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
211 /* ------------------------
214 * -----------------------
217 ExecEndGroup(GroupState *node)
219 PlanState *outerPlan;
221 ExecFreeProjectionInfo(&node->ss.ps);
222 ExecFreeExprContext(&node->ss.ps);
224 outerPlan = outerPlanState(node);
225 ExecEndNode(outerPlan);
227 /* clean up tuple table */
228 ExecClearTuple(node->ss.ss_ScanTupleSlot);
229 if (node->grp_firstTuple != NULL)
231 heap_freetuple(node->grp_firstTuple);
232 node->grp_firstTuple = NULL;
237 ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
239 node->grp_done = FALSE;
240 if (node->grp_firstTuple != NULL)
242 heap_freetuple(node->grp_firstTuple);
243 node->grp_firstTuple = NULL;
246 if (((PlanState *) node)->lefttree &&
247 ((PlanState *) node)->lefttree->chgParam == NULL)
248 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
251 /*****************************************************************************
252 * Code shared with nodeUnique.c and nodeAgg.c
253 *****************************************************************************/
257 * Return true if two tuples match in all the indicated fields.
258 * This is used to detect group boundaries in nodeGroup and nodeAgg,
259 * and to decide whether two tuples are distinct or not in nodeUnique.
261 * tuple1, tuple2: the tuples to compare
262 * tupdesc: tuple descriptor applying to both tuples
263 * numCols: the number of attributes to be examined
264 * matchColIdx: array of attribute column numbers
265 * eqFunctions: array of fmgr lookup info for the equality functions to use
266 * evalContext: short-term memory context for executing the functions
268 * NB: evalContext is reset each time!
271 execTuplesMatch(HeapTuple tuple1,
275 AttrNumber *matchColIdx,
276 FmgrInfo *eqfunctions,
277 MemoryContext evalContext)
279 MemoryContext oldContext;
283 /* Reset and switch into the temp context. */
284 MemoryContextReset(evalContext);
285 oldContext = MemoryContextSwitchTo(evalContext);
288 * We cannot report a match without checking all the fields, but we
289 * can report a non-match as soon as we find unequal fields. So,
290 * start comparing at the last field (least significant sort key).
291 * That's the most likely to be different if we are dealing with
296 for (i = numCols; --i >= 0;)
298 AttrNumber att = matchColIdx[i];
304 attr1 = heap_getattr(tuple1,
309 attr2 = heap_getattr(tuple2,
314 if (isNull1 != isNull2)
316 result = false; /* one null and one not; they aren't equal */
321 continue; /* both are null, treat as equal */
323 /* Apply the type-specific equality function */
325 if (!DatumGetBool(FunctionCall2(&eqfunctions[i],
328 result = false; /* they aren't equal */
333 MemoryContextSwitchTo(oldContext);
339 * execTuplesMatchPrepare
340 * Look up the equality functions needed for execTuplesMatch.
341 * The result is a palloc'd array.
344 execTuplesMatchPrepare(TupleDesc tupdesc,
346 AttrNumber *matchColIdx)
348 FmgrInfo *eqfunctions = (FmgrInfo *) palloc(numCols * sizeof(FmgrInfo));
351 for (i = 0; i < numCols; i++)
353 AttrNumber att = matchColIdx[i];
354 Oid typid = tupdesc->attrs[att - 1]->atttypid;
357 eq_function = equality_oper_funcid(typid);
358 fmgr_info(eq_function, &eqfunctions[i]);