1 /*-------------------------------------------------------------------------
4 * Routines to handle group nodes (used for queries with GROUP BY clause).
6 * Portions Copyright (c) 1996-2008, 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.
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 * Check to see if we're still projecting out tuples from a previous group
54 * tuple (because there is a function-returning-set in the projection
55 * expressions). If so, try to project another one.
57 if (node
->ss
.ps
.ps_TupFromTlist
)
59 TupleTableSlot
*result
;
62 result
= ExecProject(node
->ss
.ps
.ps_ProjInfo
, &isDone
);
63 if (isDone
== ExprMultipleResult
)
65 /* Done with that source tuple... */
66 node
->ss
.ps
.ps_TupFromTlist
= false;
70 * The ScanTupleSlot holds the (copied) first tuple of each group.
72 firsttupleslot
= node
->ss
.ss_ScanTupleSlot
;
75 * We need not call ResetExprContext here because execTuplesMatch will
76 * reset the per-tuple memory context once per input tuple.
80 * If first time through, acquire first input tuple and determine whether
81 * to return it or not.
83 if (TupIsNull(firsttupleslot
))
85 outerslot
= ExecProcNode(outerPlanState(node
));
86 if (TupIsNull(outerslot
))
88 /* empty input, so return nothing */
89 node
->grp_done
= TRUE
;
92 /* Copy tuple into firsttupleslot */
93 ExecCopySlot(firsttupleslot
, outerslot
);
96 * Set it up as input for qual test and projection. The expressions
97 * will access the input tuple as varno OUTER.
99 econtext
->ecxt_outertuple
= firsttupleslot
;
102 * Check the qual (HAVING clause); if the group does not match, ignore
103 * it and fall into scan loop.
105 if (ExecQual(node
->ss
.ps
.qual
, econtext
, false))
108 * Form and return a projection tuple using the first input tuple.
110 TupleTableSlot
*result
;
113 result
= ExecProject(node
->ss
.ps
.ps_ProjInfo
, &isDone
);
115 if (isDone
!= ExprEndResult
)
117 node
->ss
.ps
.ps_TupFromTlist
= (isDone
== ExprMultipleResult
);
124 * This loop iterates once per input tuple group. At the head of the
125 * loop, we have finished processing the first tuple of the group and now
126 * need to scan over all the other group members.
131 * Scan over all remaining tuples that belong to this group
135 outerslot
= ExecProcNode(outerPlanState(node
));
136 if (TupIsNull(outerslot
))
138 /* no more groups, so we're done */
139 node
->grp_done
= TRUE
;
144 * Compare with first tuple and see if this tuple is of the same
145 * group. If so, ignore it and keep scanning.
147 if (!execTuplesMatch(firsttupleslot
, outerslot
,
150 econtext
->ecxt_per_tuple_memory
))
155 * We have the first tuple of the next input group. See if we want to
158 /* Copy tuple, set up as input for qual test and projection */
159 ExecCopySlot(firsttupleslot
, outerslot
);
160 econtext
->ecxt_outertuple
= firsttupleslot
;
163 * Check the qual (HAVING clause); if the group does not match, ignore
164 * it and loop back to scan the rest of the group.
166 if (ExecQual(node
->ss
.ps
.qual
, econtext
, false))
169 * Form and return a projection tuple using the first input tuple.
171 TupleTableSlot
*result
;
174 result
= ExecProject(node
->ss
.ps
.ps_ProjInfo
, &isDone
);
176 if (isDone
!= ExprEndResult
)
178 node
->ss
.ps
.ps_TupFromTlist
= (isDone
== ExprMultipleResult
);
191 * Creates the run-time information for the group node produced by the
192 * planner and initializes its outer subtree
196 ExecInitGroup(Group
*node
, EState
*estate
, int eflags
)
198 GroupState
*grpstate
;
200 /* check for unsupported flags */
201 Assert(!(eflags
& (EXEC_FLAG_BACKWARD
| EXEC_FLAG_MARK
)));
204 * create state structure
206 grpstate
= makeNode(GroupState
);
207 grpstate
->ss
.ps
.plan
= (Plan
*) node
;
208 grpstate
->ss
.ps
.state
= estate
;
209 grpstate
->grp_done
= FALSE
;
212 * create expression context
214 ExecAssignExprContext(estate
, &grpstate
->ss
.ps
);
216 #define GROUP_NSLOTS 2
219 * tuple table initialization
221 ExecInitScanTupleSlot(estate
, &grpstate
->ss
);
222 ExecInitResultTupleSlot(estate
, &grpstate
->ss
.ps
);
225 * initialize child expressions
227 grpstate
->ss
.ps
.targetlist
= (List
*)
228 ExecInitExpr((Expr
*) node
->plan
.targetlist
,
229 (PlanState
*) grpstate
);
230 grpstate
->ss
.ps
.qual
= (List
*)
231 ExecInitExpr((Expr
*) node
->plan
.qual
,
232 (PlanState
*) grpstate
);
235 * initialize child nodes
237 outerPlanState(grpstate
) = ExecInitNode(outerPlan(node
), estate
, eflags
);
240 * initialize tuple type.
242 ExecAssignScanTypeFromOuterPlan(&grpstate
->ss
);
245 * Initialize result tuple type and projection info.
247 ExecAssignResultTypeFromTL(&grpstate
->ss
.ps
);
248 ExecAssignProjectionInfo(&grpstate
->ss
.ps
, NULL
);
250 grpstate
->ss
.ps
.ps_TupFromTlist
= false;
253 * Precompute fmgr lookup data for inner loop
255 grpstate
->eqfunctions
=
256 execTuplesMatchPrepare(node
->numCols
,
263 ExecCountSlotsGroup(Group
*node
)
265 return ExecCountSlotsNode(outerPlan(node
)) + GROUP_NSLOTS
;
268 /* ------------------------
271 * -----------------------
274 ExecEndGroup(GroupState
*node
)
276 PlanState
*outerPlan
;
278 ExecFreeExprContext(&node
->ss
.ps
);
280 /* clean up tuple table */
281 ExecClearTuple(node
->ss
.ss_ScanTupleSlot
);
283 outerPlan
= outerPlanState(node
);
284 ExecEndNode(outerPlan
);
288 ExecReScanGroup(GroupState
*node
, ExprContext
*exprCtxt
)
290 node
->grp_done
= FALSE
;
291 node
->ss
.ps
.ps_TupFromTlist
= false;
292 /* must clear first tuple */
293 ExecClearTuple(node
->ss
.ss_ScanTupleSlot
);
295 if (((PlanState
*) node
)->lefttree
&&
296 ((PlanState
*) node
)->lefttree
->chgParam
== NULL
)
297 ExecReScan(((PlanState
*) node
)->lefttree
, exprCtxt
);