Add support for user-defined I/O conversion casts.
[PostgreSQL.git] / src / backend / executor / nodeGroup.c
blob20671083fb1f3e084c4d772a816ebdd17c386f82
1 /*-------------------------------------------------------------------------
3 * nodeGroup.c
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
10 * DESCRIPTION
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.
17 * IDENTIFICATION
18 * $PostgreSQL$
20 *-------------------------------------------------------------------------
23 #include "postgres.h"
25 #include "executor/executor.h"
26 #include "executor/nodeGroup.h"
30 * ExecGroup -
32 * Return one tuple for each group of matching input tuples.
34 TupleTableSlot *
35 ExecGroup(GroupState *node)
37 ExprContext *econtext;
38 int numCols;
39 AttrNumber *grpColIdx;
40 TupleTableSlot *firsttupleslot;
41 TupleTableSlot *outerslot;
44 * get state info from node
46 if (node->grp_done)
47 return NULL;
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;
60 ExprDoneCond isDone;
62 result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
63 if (isDone == ExprMultipleResult)
64 return result;
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;
90 return NULL;
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;
111 ExprDoneCond isDone;
113 result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
115 if (isDone != ExprEndResult)
117 node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
118 return result;
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.
128 for (;;)
131 * Scan over all remaining tuples that belong to this group
133 for (;;)
135 outerslot = ExecProcNode(outerPlanState(node));
136 if (TupIsNull(outerslot))
138 /* no more groups, so we're done */
139 node->grp_done = TRUE;
140 return NULL;
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,
148 numCols, grpColIdx,
149 node->eqfunctions,
150 econtext->ecxt_per_tuple_memory))
151 break;
155 * We have the first tuple of the next input group. See if we want to
156 * return it.
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;
172 ExprDoneCond isDone;
174 result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
176 if (isDone != ExprEndResult)
178 node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
179 return result;
184 /* NOTREACHED */
185 return NULL;
188 /* -----------------
189 * ExecInitGroup
191 * Creates the run-time information for the group node produced by the
192 * planner and initializes its outer subtree
193 * -----------------
195 GroupState *
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,
257 node->grpOperators);
259 return grpstate;
263 ExecCountSlotsGroup(Group *node)
265 return ExecCountSlotsNode(outerPlan(node)) + GROUP_NSLOTS;
268 /* ------------------------
269 * ExecEndGroup(node)
271 * -----------------------
273 void
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);
287 void
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);