1 /*-------------------------------------------------------------------------
2  *
3  * nodeGroup.c
4  *	  Routines to handle group nodes (used for queries with GROUP BY clause).
5  *
6  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
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.
16  *
17  * IDENTIFICATION
18  *	  src/backend/executor/nodeGroup.c
19  *
20  *-------------------------------------------------------------------------
21  */
22 
23 #include "postgres.h"
24 
25 #include "executor/executor.h"
26 #include "executor/nodeGroup.h"
27 
28 
29 /*
30  *	 ExecGroup -
31  *
32  *		Return one tuple for each group of matching input tuples.
33  */
34 TupleTableSlot *
ExecGroup(GroupState * node)35 ExecGroup(GroupState *node)
36 {
37 	ExprContext *econtext;
38 	int			numCols;
39 	AttrNumber *grpColIdx;
40 	TupleTableSlot *firsttupleslot;
41 	TupleTableSlot *outerslot;
42 
43 	/*
44 	 * get state info from node
45 	 */
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;
51 
52 	/*
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.
56 	 */
57 	if (node->ss.ps.ps_TupFromTlist)
58 	{
59 		TupleTableSlot *result;
60 		ExprDoneCond isDone;
61 
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;
67 	}
68 
69 	/*
70 	 * The ScanTupleSlot holds the (copied) first tuple of each group.
71 	 */
72 	firsttupleslot = node->ss.ss_ScanTupleSlot;
73 
74 	/*
75 	 * We need not call ResetExprContext here because execTuplesMatch will
76 	 * reset the per-tuple memory context once per input tuple.
77 	 */
78 
79 	/*
80 	 * If first time through, acquire first input tuple and determine whether
81 	 * to return it or not.
82 	 */
83 	if (TupIsNull(firsttupleslot))
84 	{
85 		outerslot = ExecProcNode(outerPlanState(node));
86 		if (TupIsNull(outerslot))
87 		{
88 			/* empty input, so return nothing */
89 			node->grp_done = TRUE;
90 			return NULL;
91 		}
92 		/* Copy tuple into firsttupleslot */
93 		ExecCopySlot(firsttupleslot, outerslot);
94 
95 		/*
96 		 * Set it up as input for qual test and projection.  The expressions
97 		 * will access the input tuple as varno OUTER.
98 		 */
99 		econtext->ecxt_outertuple = firsttupleslot;
100 
101 		/*
102 		 * Check the qual (HAVING clause); if the group does not match, ignore
103 		 * it and fall into scan loop.
104 		 */
105 		if (ExecQual(node->ss.ps.qual, econtext, false))
106 		{
107 			/*
108 			 * Form and return a projection tuple using the first input tuple.
109 			 */
110 			TupleTableSlot *result;
111 			ExprDoneCond isDone;
112 
113 			result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
114 
115 			if (isDone != ExprEndResult)
116 			{
117 				node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
118 				return result;
119 			}
120 		}
121 		else
122 			InstrCountFiltered1(node, 1);
123 	}
124 
125 	/*
126 	 * This loop iterates once per input tuple group.  At the head of the
127 	 * loop, we have finished processing the first tuple of the group and now
128 	 * need to scan over all the other group members.
129 	 */
130 	for (;;)
131 	{
132 		/*
133 		 * Scan over all remaining tuples that belong to this group
134 		 */
135 		for (;;)
136 		{
137 			outerslot = ExecProcNode(outerPlanState(node));
138 			if (TupIsNull(outerslot))
139 			{
140 				/* no more groups, so we're done */
141 				node->grp_done = TRUE;
142 				return NULL;
143 			}
144 
145 			/*
146 			 * Compare with first tuple and see if this tuple is of the same
147 			 * group.  If so, ignore it and keep scanning.
148 			 */
149 			if (!execTuplesMatch(firsttupleslot, outerslot,
150 								 numCols, grpColIdx,
151 								 node->eqfunctions,
152 								 econtext->ecxt_per_tuple_memory))
153 				break;
154 		}
155 
156 		/*
157 		 * We have the first tuple of the next input group.  See if we want to
158 		 * return it.
159 		 */
160 		/* Copy tuple, set up as input for qual test and projection */
161 		ExecCopySlot(firsttupleslot, outerslot);
162 		econtext->ecxt_outertuple = firsttupleslot;
163 
164 		/*
165 		 * Check the qual (HAVING clause); if the group does not match, ignore
166 		 * it and loop back to scan the rest of the group.
167 		 */
168 		if (ExecQual(node->ss.ps.qual, econtext, false))
169 		{
170 			/*
171 			 * Form and return a projection tuple using the first input tuple.
172 			 */
173 			TupleTableSlot *result;
174 			ExprDoneCond isDone;
175 
176 			result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone);
177 
178 			if (isDone != ExprEndResult)
179 			{
180 				node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
181 				return result;
182 			}
183 		}
184 		else
185 			InstrCountFiltered1(node, 1);
186 	}
187 }
188 
189 /* -----------------
190  * ExecInitGroup
191  *
192  *	Creates the run-time information for the group node produced by the
193  *	planner and initializes its outer subtree
194  * -----------------
195  */
196 GroupState *
ExecInitGroup(Group * node,EState * estate,int eflags)197 ExecInitGroup(Group *node, EState *estate, int eflags)
198 {
199 	GroupState *grpstate;
200 
201 	/* check for unsupported flags */
202 	Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
203 
204 	/*
205 	 * create state structure
206 	 */
207 	grpstate = makeNode(GroupState);
208 	grpstate->ss.ps.plan = (Plan *) node;
209 	grpstate->ss.ps.state = estate;
210 	grpstate->grp_done = FALSE;
211 
212 	/*
213 	 * create expression context
214 	 */
215 	ExecAssignExprContext(estate, &grpstate->ss.ps);
216 
217 	/*
218 	 * tuple table initialization
219 	 */
220 	ExecInitScanTupleSlot(estate, &grpstate->ss);
221 	ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
222 
223 	/*
224 	 * initialize child expressions
225 	 */
226 	grpstate->ss.ps.targetlist = (List *)
227 		ExecInitExpr((Expr *) node->plan.targetlist,
228 					 (PlanState *) grpstate);
229 	grpstate->ss.ps.qual = (List *)
230 		ExecInitExpr((Expr *) node->plan.qual,
231 					 (PlanState *) grpstate);
232 
233 	/*
234 	 * initialize child nodes
235 	 */
236 	outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
237 
238 	/*
239 	 * initialize tuple type.
240 	 */
241 	ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
242 
243 	/*
244 	 * Initialize result tuple type and projection info.
245 	 */
246 	ExecAssignResultTypeFromTL(&grpstate->ss.ps);
247 	ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
248 
249 	grpstate->ss.ps.ps_TupFromTlist = false;
250 
251 	/*
252 	 * Precompute fmgr lookup data for inner loop
253 	 */
254 	grpstate->eqfunctions =
255 		execTuplesMatchPrepare(node->numCols,
256 							   node->grpOperators);
257 
258 	return grpstate;
259 }
260 
261 /* ------------------------
262  *		ExecEndGroup(node)
263  *
264  * -----------------------
265  */
266 void
ExecEndGroup(GroupState * node)267 ExecEndGroup(GroupState *node)
268 {
269 	PlanState  *outerPlan;
270 
271 	ExecFreeExprContext(&node->ss.ps);
272 
273 	/* clean up tuple table */
274 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
275 
276 	outerPlan = outerPlanState(node);
277 	ExecEndNode(outerPlan);
278 }
279 
280 void
ExecReScanGroup(GroupState * node)281 ExecReScanGroup(GroupState *node)
282 {
283 	PlanState  *outerPlan = outerPlanState(node);
284 
285 	node->grp_done = FALSE;
286 	node->ss.ps.ps_TupFromTlist = false;
287 	/* must clear first tuple */
288 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
289 
290 	/*
291 	 * if chgParam of subnode is not null then plan will be re-scanned by
292 	 * first ExecProcNode.
293 	 */
294 	if (outerPlan->chgParam == NULL)
295 		ExecReScan(outerPlan);
296 }
297