1 /*-------------------------------------------------------------------------
2  *
3  * nodeGroup.c
4  *	  Routines to handle group nodes (used for queries with GROUP BY clause).
5  *
6  * Portions Copyright (c) 1996-2019, 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 #include "miscadmin.h"
28 #include "utils/memutils.h"
29 
30 
31 /*
32  *	 ExecGroup -
33  *
34  *		Return one tuple for each group of matching input tuples.
35  */
36 static TupleTableSlot *
ExecGroup(PlanState * pstate)37 ExecGroup(PlanState *pstate)
38 {
39 	GroupState *node = castNode(GroupState, pstate);
40 	ExprContext *econtext;
41 	TupleTableSlot *firsttupleslot;
42 	TupleTableSlot *outerslot;
43 
44 	CHECK_FOR_INTERRUPTS();
45 
46 	/*
47 	 * get state info from node
48 	 */
49 	if (node->grp_done)
50 		return NULL;
51 	econtext = node->ss.ps.ps_ExprContext;
52 
53 	/*
54 	 * The ScanTupleSlot holds the (copied) first tuple of each group.
55 	 */
56 	firsttupleslot = node->ss.ss_ScanTupleSlot;
57 
58 	/*
59 	 * We need not call ResetExprContext here because ExecQualAndReset() will
60 	 * reset the per-tuple memory context once per input tuple.
61 	 */
62 
63 	/*
64 	 * If first time through, acquire first input tuple and determine whether
65 	 * to return it or not.
66 	 */
67 	if (TupIsNull(firsttupleslot))
68 	{
69 		outerslot = ExecProcNode(outerPlanState(node));
70 		if (TupIsNull(outerslot))
71 		{
72 			/* empty input, so return nothing */
73 			node->grp_done = true;
74 			return NULL;
75 		}
76 		/* Copy tuple into firsttupleslot */
77 		ExecCopySlot(firsttupleslot, outerslot);
78 
79 		/*
80 		 * Set it up as input for qual test and projection.  The expressions
81 		 * will access the input tuple as varno OUTER.
82 		 */
83 		econtext->ecxt_outertuple = firsttupleslot;
84 
85 		/*
86 		 * Check the qual (HAVING clause); if the group does not match, ignore
87 		 * it and fall into scan loop.
88 		 */
89 		if (ExecQual(node->ss.ps.qual, econtext))
90 		{
91 			/*
92 			 * Form and return a projection tuple using the first input tuple.
93 			 */
94 			return ExecProject(node->ss.ps.ps_ProjInfo);
95 		}
96 		else
97 			InstrCountFiltered1(node, 1);
98 	}
99 
100 	/*
101 	 * This loop iterates once per input tuple group.  At the head of the
102 	 * loop, we have finished processing the first tuple of the group and now
103 	 * need to scan over all the other group members.
104 	 */
105 	for (;;)
106 	{
107 		/*
108 		 * Scan over all remaining tuples that belong to this group
109 		 */
110 		for (;;)
111 		{
112 			outerslot = ExecProcNode(outerPlanState(node));
113 			if (TupIsNull(outerslot))
114 			{
115 				/* no more groups, so we're done */
116 				node->grp_done = true;
117 				return NULL;
118 			}
119 
120 			/*
121 			 * Compare with first tuple and see if this tuple is of the same
122 			 * group.  If so, ignore it and keep scanning.
123 			 */
124 			econtext->ecxt_innertuple = firsttupleslot;
125 			econtext->ecxt_outertuple = outerslot;
126 			if (!ExecQualAndReset(node->eqfunction, econtext))
127 				break;
128 		}
129 
130 		/*
131 		 * We have the first tuple of the next input group.  See if we want to
132 		 * return it.
133 		 */
134 		/* Copy tuple, set up as input for qual test and projection */
135 		ExecCopySlot(firsttupleslot, outerslot);
136 		econtext->ecxt_outertuple = firsttupleslot;
137 
138 		/*
139 		 * Check the qual (HAVING clause); if the group does not match, ignore
140 		 * it and loop back to scan the rest of the group.
141 		 */
142 		if (ExecQual(node->ss.ps.qual, econtext))
143 		{
144 			/*
145 			 * Form and return a projection tuple using the first input tuple.
146 			 */
147 			return ExecProject(node->ss.ps.ps_ProjInfo);
148 		}
149 		else
150 			InstrCountFiltered1(node, 1);
151 	}
152 }
153 
154 /* -----------------
155  * ExecInitGroup
156  *
157  *	Creates the run-time information for the group node produced by the
158  *	planner and initializes its outer subtree
159  * -----------------
160  */
161 GroupState *
ExecInitGroup(Group * node,EState * estate,int eflags)162 ExecInitGroup(Group *node, EState *estate, int eflags)
163 {
164 	GroupState *grpstate;
165 	const TupleTableSlotOps *tts_ops;
166 
167 	/* check for unsupported flags */
168 	Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
169 
170 	/*
171 	 * create state structure
172 	 */
173 	grpstate = makeNode(GroupState);
174 	grpstate->ss.ps.plan = (Plan *) node;
175 	grpstate->ss.ps.state = estate;
176 	grpstate->ss.ps.ExecProcNode = ExecGroup;
177 	grpstate->grp_done = false;
178 
179 	/*
180 	 * create expression context
181 	 */
182 	ExecAssignExprContext(estate, &grpstate->ss.ps);
183 
184 	/*
185 	 * initialize child nodes
186 	 */
187 	outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate, eflags);
188 
189 	/*
190 	 * Initialize scan slot and type.
191 	 */
192 	tts_ops = ExecGetResultSlotOps(outerPlanState(&grpstate->ss), NULL);
193 	ExecCreateScanSlotFromOuterPlan(estate, &grpstate->ss, tts_ops);
194 
195 	/*
196 	 * Initialize result slot, type and projection.
197 	 */
198 	ExecInitResultTupleSlotTL(&grpstate->ss.ps, &TTSOpsVirtual);
199 	ExecAssignProjectionInfo(&grpstate->ss.ps, NULL);
200 
201 	/*
202 	 * initialize child expressions
203 	 */
204 	grpstate->ss.ps.qual =
205 		ExecInitQual(node->plan.qual, (PlanState *) grpstate);
206 
207 	/*
208 	 * Precompute fmgr lookup data for inner loop
209 	 */
210 	grpstate->eqfunction =
211 		execTuplesMatchPrepare(ExecGetResultType(outerPlanState(grpstate)),
212 							   node->numCols,
213 							   node->grpColIdx,
214 							   node->grpOperators,
215 							   node->grpCollations,
216 							   &grpstate->ss.ps);
217 
218 	return grpstate;
219 }
220 
221 /* ------------------------
222  *		ExecEndGroup(node)
223  *
224  * -----------------------
225  */
226 void
ExecEndGroup(GroupState * node)227 ExecEndGroup(GroupState *node)
228 {
229 	PlanState  *outerPlan;
230 
231 	ExecFreeExprContext(&node->ss.ps);
232 
233 	/* clean up tuple table */
234 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
235 
236 	outerPlan = outerPlanState(node);
237 	ExecEndNode(outerPlan);
238 }
239 
240 void
ExecReScanGroup(GroupState * node)241 ExecReScanGroup(GroupState *node)
242 {
243 	PlanState  *outerPlan = outerPlanState(node);
244 
245 	node->grp_done = false;
246 	/* must clear first tuple */
247 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
248 
249 	/*
250 	 * if chgParam of subnode is not null then plan will be re-scanned by
251 	 * first ExecProcNode.
252 	 */
253 	if (outerPlan->chgParam == NULL)
254 		ExecReScan(outerPlan);
255 }
256