1 /*-------------------------------------------------------------------------
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Portions Copyright (c) 1996-2007, 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 * $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.69 2007/02/22 23:44:24 tgl Exp $
20 *-------------------------------------------------------------------------
25 #include "executor/executor.h"
26 #include "executor/nodeGroup.h"
32 * Return one tuple for each group of matching input tuples.
35 ExecGroup(GroupState *node)
37 ExprContext *econtext;
39 AttrNumber *grpColIdx;
40 TupleTableSlot *firsttupleslot;
41 TupleTableSlot *outerslot;
44 * get state info from node
48 econtext = node->ss.ps.ps_ExprContext;
49 numCols = ((Group *) node->ss.ps.plan)->numCols;
50 grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
53 * The ScanTupleSlot holds the (copied) first tuple of each group.
55 firsttupleslot = node->ss.ss_ScanTupleSlot;
58 * We need not call ResetExprContext here because execTuplesMatch will
59 * reset the per-tuple memory context once per input tuple.
63 * If first time through, acquire first input tuple and determine whether
64 * to return it or not.
66 if (TupIsNull(firsttupleslot))
68 outerslot = ExecProcNode(outerPlanState(node));
69 if (TupIsNull(outerslot))
71 /* empty input, so return nothing */
72 node->grp_done = TRUE;
75 /* Copy tuple into firsttupleslot */
76 ExecCopySlot(firsttupleslot, outerslot);
79 * Set it up as input for qual test and projection. The expressions
80 * will access the input tuple as varno OUTER.
82 econtext->ecxt_outertuple = firsttupleslot;
85 * Check the qual (HAVING clause); if the group does not match, ignore
86 * it and fall into scan loop.
88 if (ExecQual(node->ss.ps.qual, econtext, false))
91 * Form and return a projection tuple using the first input tuple.
93 return ExecProject(node->ss.ps.ps_ProjInfo, NULL);
98 * This loop iterates once per input tuple group. At the head of the
99 * loop, we have finished processing the first tuple of the group and now
100 * need to scan over all the other group members.
105 * Scan over all remaining tuples that belong to this group
109 outerslot = ExecProcNode(outerPlanState(node));
110 if (TupIsNull(outerslot))
112 /* no more groups, so we're done */
113 node->grp_done = TRUE;
118 * Compare with first tuple and see if this tuple is of the same
119 * group. If so, ignore it and keep scanning.
121 if (!execTuplesMatch(firsttupleslot, outerslot,
124 econtext->ecxt_per_tuple_memory))
129 * We have the first tuple of the next input group. See if we want to
132 /* Copy tuple, set up as input for qual test and projection */
133 ExecCopySlot(firsttupleslot, outerslot);
134 econtext->ecxt_outertuple = firsttupleslot;
137 * Check the qual (HAVING clause); if the group does not match, ignore
138 * it and loop back to scan the rest of the group.
140 if (ExecQual(node->ss.ps.qual, econtext, false))
143 * Form and return a projection tuple using the first input tuple.
145 return ExecProject(node->ss.ps.ps_ProjInfo, NULL);
156 * Creates the run-time information for the group node produced by the
157 * planner and initializes its outer subtree
161 ExecInitGroup(Group *node, EState *estate, int eflags)
163 GroupState *grpstate;
165 /* check for unsupported flags */
166 Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
169 * create state structure
171 grpstate = makeNode(GroupState);
172 grpstate->ss.ps.plan = (Plan *) node;
173 grpstate->ss.ps.state = estate;
174 grpstate->grp_done = FALSE;
177 * create expression context
179 ExecAssignExprContext(estate, &grpstate->ss.ps);
181 #define GROUP_NSLOTS 2
184 * tuple table initialization
186 ExecInitScanTupleSlot(estate, &grpstate->ss);
187 ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
190 * initialize child expressions
192 grpstate->ss.ps.targetlist = (List *)
193 ExecInitExpr((Expr *) node->plan.targetlist,
194 (PlanState *) grpstate);
195 grpstate->ss.ps.qual = (List *)
196 ExecInitExpr((Expr *) node->plan.qual,
197 (PlanState *) grpstate);
200 * initialize child nodes
202 outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
205 * initialize tuple type.
207 ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
210 * Initialize result tuple type and projection info.
212 ExecAssignResultTypeFromTL(&grpstate->ss.ps);
213 ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
216 * Precompute fmgr lookup data for inner loop
218 grpstate->eqfunctions =
219 execTuplesMatchPrepare(node->numCols,
226 ExecCountSlotsGroup(Group *node)
228 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
231 /* ------------------------
234 * -----------------------
237 ExecEndGroup(GroupState *node)
239 PlanState *outerPlan;
241 ExecFreeExprContext(&node->ss.ps);
243 /* clean up tuple table */
244 ExecClearTuple(node->ss.ss_ScanTupleSlot);
246 outerPlan = outerPlanState(node);
247 ExecEndNode(outerPlan);
251 ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
253 node->grp_done = FALSE;
254 /* must clear first tuple */
255 ExecClearTuple(node->ss.ss_ScanTupleSlot);
257 if (((PlanState *) node)->lefttree &&
258 ((PlanState *) node)->lefttree->chgParam == NULL)
259 ExecReScan(((PlanState *) node)->lefttree, exprCtxt);