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-2019, 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
32static 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 */
42static TupleTableSlot *
43ExecProjectSet(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 */
131static TupleTableSlot *
132ExecProjectSRF(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 */
219ProjectSetState *
220ExecInitProjectSet(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(&state->ps, &TTSOpsVirtual);
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 ExecMakeFunctionResultSet can use to
301 * evaluate function arguments in. We can't use the per-tuple context for
302 * this because it gets reset too often; but we don't want to leak
303 * evaluation results into the query-lifespan context either. We use one
304 * context for the arguments of all tSRFs, as they have roughly equivalent
305 * lifetimes.
306 */
307 state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
308 "tSRF function arguments",
309 ALLOCSET_DEFAULT_SIZES);
310
311 return state;
312}
313
314/* ----------------------------------------------------------------
315 * ExecEndProjectSet
316 *
317 * frees up storage allocated through C routines
318 * ----------------------------------------------------------------
319 */
320void
321ExecEndProjectSet(ProjectSetState *node)
322{
323 /*
324 * Free the exprcontext
325 */
326 ExecFreeExprContext(&node->ps);
327
328 /*
329 * clean out the tuple table
330 */
331 ExecClearTuple(node->ps.ps_ResultTupleSlot);
332
333 /*
334 * shut down subplans
335 */
336 ExecEndNode(outerPlanState(node));
337}
338
339void
340ExecReScanProjectSet(ProjectSetState *node)
341{
342 /* Forget any incompletely-evaluated SRFs */
343 node->pending_srf_tuples = false;
344
345 /*
346 * If chgParam of subnode is not null then plan will be re-scanned by
347 * first ExecProcNode.
348 */
349 if (node->ps.lefttree->chgParam == NULL)
350 ExecReScan(node->ps.lefttree);
351}
352