1 /*-------------------------------------------------------------------------
2  *
3  * nodeProjectSet.c
4  *	  support for evaluating targetlists containing set-returning functions
5  *
6  * DESCRIPTION
7  *
8  *		ProjectSet nodes are inserted by the planner to evaluate set-returning
9  *		functions in the targetlist.  It's guaranteed that all set-returning
10  *		functions are directly at the top level of the targetlist, i.e. they
11  *		can't be inside more-complex expressions.  If that'd otherwise be
12  *		the case, the planner adds additional ProjectSet nodes.
13  *
14  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
15  * Portions Copyright (c) 1994, Regents of the University of California
16  *
17  * IDENTIFICATION
18  *	  src/backend/executor/nodeProjectSet.c
19  *
20  *-------------------------------------------------------------------------
21  */
22 
23 #include "postgres.h"
24 
25 #include "executor/executor.h"
26 #include "executor/nodeProjectSet.h"
27 #include "miscadmin.h"
28 #include "nodes/nodeFuncs.h"
29 #include "utils/memutils.h"
30 
31 
32 static TupleTableSlot *ExecProjectSRF(ProjectSetState *node, bool continuing);
33 
34 
35 /* ----------------------------------------------------------------
36  *		ExecProjectSet(node)
37  *
38  *		Return tuples after evaluating the targetlist (which contains set
39  *		returning functions).
40  * ----------------------------------------------------------------
41  */
42 static TupleTableSlot *
ExecProjectSet(PlanState * pstate)43 ExecProjectSet(PlanState *pstate)
44 {
45 	ProjectSetState *node = castNode(ProjectSetState, pstate);
46 	TupleTableSlot *outerTupleSlot;
47 	TupleTableSlot *resultSlot;
48 	PlanState  *outerPlan;
49 	ExprContext *econtext;
50 
51 	CHECK_FOR_INTERRUPTS();
52 
53 	econtext = node->ps.ps_ExprContext;
54 
55 	/*
56 	 * Reset per-tuple context to free expression-evaluation storage allocated
57 	 * for a potentially previously returned tuple. Note that the SRF argument
58 	 * context has a different lifetime and is reset below.
59 	 */
60 	ResetExprContext(econtext);
61 
62 	/*
63 	 * Check to see if we're still projecting out tuples from a previous scan
64 	 * tuple (because there is a function-returning-set in the projection
65 	 * expressions).  If so, try to project another one.
66 	 */
67 	if (node->pending_srf_tuples)
68 	{
69 		resultSlot = ExecProjectSRF(node, true);
70 
71 		if (resultSlot != NULL)
72 			return resultSlot;
73 	}
74 
75 	/*
76 	 * Reset argument context to free any expression evaluation storage
77 	 * allocated in the previous tuple cycle.  Note this can't happen until
78 	 * we're done projecting out tuples from a scan tuple, as ValuePerCall
79 	 * functions are allowed to reference the arguments for each returned
80 	 * tuple.
81 	 */
82 	MemoryContextReset(node->argcontext);
83 
84 	/*
85 	 * Get another input tuple and project SRFs from it.
86 	 */
87 	for (;;)
88 	{
89 		/*
90 		 * Retrieve tuples from the outer plan until there are no more.
91 		 */
92 		outerPlan = outerPlanState(node);
93 		outerTupleSlot = ExecProcNode(outerPlan);
94 
95 		if (TupIsNull(outerTupleSlot))
96 			return NULL;
97 
98 		/*
99 		 * Prepare to compute projection expressions, which will expect to
100 		 * access the input tuples as varno OUTER.
101 		 */
102 		econtext->ecxt_outertuple = outerTupleSlot;
103 
104 		/* Evaluate the expressions */
105 		resultSlot = ExecProjectSRF(node, false);
106 
107 		/*
108 		 * Return the tuple unless the projection produced no rows (due to an
109 		 * empty set), in which case we must loop back to see if there are
110 		 * more outerPlan tuples.
111 		 */
112 		if (resultSlot)
113 			return resultSlot;
114 	}
115 
116 	return NULL;
117 }
118 
119 /* ----------------------------------------------------------------
120  *		ExecProjectSRF
121  *
122  *		Project a targetlist containing one or more set-returning functions.
123  *
124  *		'continuing' indicates whether to continue projecting rows for the
125  *		same input tuple; or whether a new input tuple is being projected.
126  *
127  *		Returns NULL if no output tuple has been produced.
128  *
129  * ----------------------------------------------------------------
130  */
131 static TupleTableSlot *
ExecProjectSRF(ProjectSetState * node,bool continuing)132 ExecProjectSRF(ProjectSetState *node, bool continuing)
133 {
134 	TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
135 	ExprContext *econtext = node->ps.ps_ExprContext;
136 	MemoryContext oldcontext;
137 	bool		hassrf PG_USED_FOR_ASSERTS_ONLY;
138 	bool		hasresult;
139 	int			argno;
140 
141 	ExecClearTuple(resultSlot);
142 
143 	/* Call SRFs, as well as plain expressions, in per-tuple context */
144 	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
145 
146 	/*
147 	 * Assume no further tuples are produced unless an ExprMultipleResult is
148 	 * encountered from a set returning function.
149 	 */
150 	node->pending_srf_tuples = false;
151 
152 	hassrf = hasresult = false;
153 	for (argno = 0; argno < node->nelems; argno++)
154 	{
155 		Node	   *elem = node->elems[argno];
156 		ExprDoneCond *isdone = &node->elemdone[argno];
157 		Datum	   *result = &resultSlot->tts_values[argno];
158 		bool	   *isnull = &resultSlot->tts_isnull[argno];
159 
160 		if (continuing && *isdone == ExprEndResult)
161 		{
162 			/*
163 			 * If we're continuing to project output rows from a source tuple,
164 			 * return NULLs once the SRF has been exhausted.
165 			 */
166 			*result = (Datum) 0;
167 			*isnull = true;
168 			hassrf = true;
169 		}
170 		else if (IsA(elem, SetExprState))
171 		{
172 			/*
173 			 * Evaluate SRF - possibly continuing previously started output.
174 			 */
175 			*result = ExecMakeFunctionResultSet((SetExprState *) elem,
176 												econtext, node->argcontext,
177 												isnull, isdone);
178 
179 			if (*isdone != ExprEndResult)
180 				hasresult = true;
181 			if (*isdone == ExprMultipleResult)
182 				node->pending_srf_tuples = true;
183 			hassrf = true;
184 		}
185 		else
186 		{
187 			/* Non-SRF tlist expression, just evaluate normally. */
188 			*result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
189 			*isdone = ExprSingleResult;
190 		}
191 	}
192 
193 	MemoryContextSwitchTo(oldcontext);
194 
195 	/* ProjectSet should not be used if there's no SRFs */
196 	Assert(hassrf);
197 
198 	/*
199 	 * If all the SRFs returned EndResult, we consider that as no row being
200 	 * produced.
201 	 */
202 	if (hasresult)
203 	{
204 		ExecStoreVirtualTuple(resultSlot);
205 		return resultSlot;
206 	}
207 
208 	return NULL;
209 }
210 
211 /* ----------------------------------------------------------------
212  *		ExecInitProjectSet
213  *
214  *		Creates the run-time state information for the ProjectSet node
215  *		produced by the planner and initializes outer relations
216  *		(child nodes).
217  * ----------------------------------------------------------------
218  */
219 ProjectSetState *
ExecInitProjectSet(ProjectSet * node,EState * estate,int eflags)220 ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
221 {
222 	ProjectSetState *state;
223 	ListCell   *lc;
224 	int			off;
225 
226 	/* check for unsupported flags */
227 	Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
228 
229 	/*
230 	 * create state structure
231 	 */
232 	state = makeNode(ProjectSetState);
233 	state->ps.plan = (Plan *) node;
234 	state->ps.state = estate;
235 	state->ps.ExecProcNode = ExecProjectSet;
236 
237 	state->pending_srf_tuples = false;
238 
239 	/*
240 	 * Miscellaneous initialization
241 	 *
242 	 * create expression context for node
243 	 */
244 	ExecAssignExprContext(estate, &state->ps);
245 
246 	/*
247 	 * initialize child nodes
248 	 */
249 	outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
250 
251 	/*
252 	 * we don't use inner plan
253 	 */
254 	Assert(innerPlan(node) == NULL);
255 
256 	/*
257 	 * tuple table and result type initialization
258 	 */
259 	ExecInitResultTupleSlotTL(estate, &state->ps);
260 
261 	/* Create workspace for per-tlist-entry expr state & SRF-is-done state */
262 	state->nelems = list_length(node->plan.targetlist);
263 	state->elems = (Node **)
264 		palloc(sizeof(Node *) * state->nelems);
265 	state->elemdone = (ExprDoneCond *)
266 		palloc(sizeof(ExprDoneCond) * state->nelems);
267 
268 	/*
269 	 * Build expressions to evaluate targetlist.  We can't use
270 	 * ExecBuildProjectionInfo here, since that doesn't deal with SRFs.
271 	 * Instead compile each expression separately, using
272 	 * ExecInitFunctionResultSet where applicable.
273 	 */
274 	off = 0;
275 	foreach(lc, node->plan.targetlist)
276 	{
277 		TargetEntry *te = (TargetEntry *) lfirst(lc);
278 		Expr	   *expr = te->expr;
279 
280 		if ((IsA(expr, FuncExpr) &&((FuncExpr *) expr)->funcretset) ||
281 			(IsA(expr, OpExpr) &&((OpExpr *) expr)->opretset))
282 		{
283 			state->elems[off] = (Node *)
284 				ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
285 										  &state->ps);
286 		}
287 		else
288 		{
289 			Assert(!expression_returns_set((Node *) expr));
290 			state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
291 		}
292 
293 		off++;
294 	}
295 
296 	/* We don't support any qual on ProjectSet nodes */
297 	Assert(node->plan.qual == NIL);
298 
299 	/*
300 	 * Create a memory context that ExecMakeFunctionResult can use to evaluate
301 	 * function arguments in.  We can't use the per-tuple context for this
302 	 * because it gets reset too often; but we don't want to leak evaluation
303 	 * results into the query-lifespan context either.  We use one context for
304 	 * the arguments of all tSRFs, as they have roughly equivalent lifetimes.
305 	 */
306 	state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
307 											  "tSRF function arguments",
308 											  ALLOCSET_DEFAULT_SIZES);
309 
310 	return state;
311 }
312 
313 /* ----------------------------------------------------------------
314  *		ExecEndProjectSet
315  *
316  *		frees up storage allocated through C routines
317  * ----------------------------------------------------------------
318  */
319 void
ExecEndProjectSet(ProjectSetState * node)320 ExecEndProjectSet(ProjectSetState *node)
321 {
322 	/*
323 	 * Free the exprcontext
324 	 */
325 	ExecFreeExprContext(&node->ps);
326 
327 	/*
328 	 * clean out the tuple table
329 	 */
330 	ExecClearTuple(node->ps.ps_ResultTupleSlot);
331 
332 	/*
333 	 * shut down subplans
334 	 */
335 	ExecEndNode(outerPlanState(node));
336 }
337 
338 void
ExecReScanProjectSet(ProjectSetState * node)339 ExecReScanProjectSet(ProjectSetState *node)
340 {
341 	/* Forget any incompletely-evaluated SRFs */
342 	node->pending_srf_tuples = false;
343 
344 	/*
345 	 * If chgParam of subnode is not null then plan will be re-scanned by
346 	 * first ExecProcNode.
347 	 */
348 	if (node->ps.lefttree->chgParam == NULL)
349 		ExecReScan(node->ps.lefttree);
350 }
351