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 | |
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 * |
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 * |
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 * |
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(&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 | */ |
320 | void |
321 | ExecEndProjectSet(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 | |
339 | void |
340 | ExecReScanProjectSet(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 | |