Age Owner TLA Line data Source code
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-2023, 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 *
2092 andres 43 CBC 948841 : ExecProjectSet(PlanState *pstate)
44 : {
45 948841 : ProjectSetState *node = castNode(ProjectSetState, pstate);
46 : TupleTableSlot *outerTupleSlot;
47 : TupleTableSlot *resultSlot;
48 : PlanState *outerPlan;
49 : ExprContext *econtext;
50 :
2084 51 948841 : CHECK_FOR_INTERRUPTS();
52 :
2272 53 948841 : 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 : */
2009 60 948841 : 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 : */
2272 67 948841 : if (node->pending_srf_tuples)
68 : {
69 906183 : resultSlot = ExecProjectSRF(node, true);
70 :
71 906183 : if (resultSlot != NULL)
72 835418 : 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 : */
2009 82 113423 : 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 : */
2272 92 144190 : outerPlan = outerPlanState(node);
93 144190 : outerTupleSlot = ExecProcNode(outerPlan);
94 :
95 144190 : if (TupIsNull(outerTupleSlot))
96 42304 : return NULL;
97 :
98 : /*
99 : * Prepare to compute projection expressions, which will expect to
100 : * access the input tuples as varno OUTER.
101 : */
102 101886 : econtext->ecxt_outertuple = outerTupleSlot;
103 :
104 : /* Evaluate the expressions */
105 101886 : 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 101583 : if (resultSlot)
113 70816 : 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 1008069 : ExecProjectSRF(ProjectSetState *node, bool continuing)
133 : {
134 1008069 : TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
135 1008069 : 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 1008069 : ExecClearTuple(resultSlot);
142 :
143 : /* Call SRFs, as well as plain expressions, in per-tuple context */
2011 tgl 144 1008069 : 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 : */
2272 andres 150 1008069 : node->pending_srf_tuples = false;
151 :
2217 152 1008069 : hassrf = hasresult = false;
153 2843185 : for (argno = 0; argno < node->nelems; argno++)
154 : {
155 1835419 : Node *elem = node->elems[argno];
2272 156 1835419 : ExprDoneCond *isdone = &node->elemdone[argno];
157 1835419 : Datum *result = &resultSlot->tts_values[argno];
158 1835419 : bool *isnull = &resultSlot->tts_isnull[argno];
159 :
160 1835419 : 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 15010 : *result = (Datum) 0;
167 15010 : *isnull = true;
168 15010 : hassrf = true;
169 : }
2217 170 1820409 : else if (IsA(elem, SetExprState))
171 : {
172 : /*
173 : * Evaluate SRF - possibly continuing previously started output.
174 : */
175 1093750 : *result = ExecMakeFunctionResultSet((SetExprState *) elem,
176 : econtext, node->argcontext,
177 : isnull, isdone);
178 :
2272 179 1093447 : if (*isdone != ExprEndResult)
180 989005 : hasresult = true;
181 1093447 : if (*isdone == ExprMultipleResult)
182 989002 : node->pending_srf_tuples = true;
183 1093447 : hassrf = true;
184 : }
185 : else
186 : {
187 : /* Non-SRF tlist expression, just evaluate normally. */
2217 188 726659 : *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
2272 189 726659 : *isdone = ExprSingleResult;
190 : }
191 : }
192 :
2011 tgl 193 1007766 : MemoryContextSwitchTo(oldcontext);
194 :
195 : /* ProjectSet should not be used if there's no SRFs */
2272 andres 196 1007766 : Assert(hassrf);
197 :
198 : /*
199 : * If all the SRFs returned ExprEndResult, we consider that as no row
200 : * being produced.
201 : */
202 1007766 : if (hasresult)
203 : {
204 906234 : ExecStoreVirtualTuple(resultSlot);
205 906234 : return resultSlot;
206 : }
207 :
208 101532 : 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 3505 : ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
221 : {
222 : ProjectSetState *state;
223 : ListCell *lc;
224 : int off;
225 :
226 : /* check for unsupported flags */
227 3505 : Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
228 :
229 : /*
230 : * create state structure
231 : */
232 3505 : state = makeNode(ProjectSetState);
233 3505 : state->ps.plan = (Plan *) node;
234 3505 : state->ps.state = estate;
2092 235 3505 : state->ps.ExecProcNode = ExecProjectSet;
236 :
2272 237 3505 : state->pending_srf_tuples = false;
238 :
239 : /*
240 : * Miscellaneous initialization
241 : *
242 : * create expression context for node
243 : */
244 3505 : ExecAssignExprContext(estate, &state->ps);
245 :
246 : /*
247 : * initialize child nodes
248 : */
249 3505 : outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
250 :
251 : /*
252 : * we don't use inner plan
253 : */
254 3505 : Assert(innerPlan(node) == NULL);
255 :
256 : /*
257 : * tuple table and result type initialization
258 : */
1606 259 3505 : ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual);
260 :
261 : /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
2272 262 3505 : state->nelems = list_length(node->plan.targetlist);
2217 263 3505 : state->elems = (Node **)
264 3505 : palloc(sizeof(Node *) * state->nelems);
2272 265 3505 : state->elemdone = (ExprDoneCond *)
266 3505 : 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 : */
2217 274 3505 : off = 0;
275 8003 : foreach(lc, node->plan.targetlist)
276 : {
277 4500 : TargetEntry *te = (TargetEntry *) lfirst(lc);
278 4500 : Expr *expr = te->expr;
279 :
1058 tgl 280 4500 : if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
281 762 : (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
282 : {
2217 andres 283 3739 : state->elems[off] = (Node *)
284 3741 : ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
285 : &state->ps);
286 : }
287 : else
288 : {
289 759 : Assert(!expression_returns_set((Node *) expr));
290 759 : state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
291 : }
292 :
293 4498 : off++;
294 : }
295 :
296 : /* We don't support any qual on ProjectSet nodes */
1878 297 3503 : 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 : */
2009 307 3503 : state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
308 : "tSRF function arguments",
309 : ALLOCSET_DEFAULT_SIZES);
310 :
2272 311 3503 : return state;
312 : }
313 :
314 : /* ----------------------------------------------------------------
315 : * ExecEndProjectSet
316 : *
317 : * frees up storage allocated through C routines
318 : * ----------------------------------------------------------------
319 : */
320 : void
321 3197 : ExecEndProjectSet(ProjectSetState *node)
322 : {
323 : /*
324 : * Free the exprcontext
325 : */
326 3197 : ExecFreeExprContext(&node->ps);
327 :
328 : /*
329 : * clean out the tuple table
330 : */
331 3197 : ExecClearTuple(node->ps.ps_ResultTupleSlot);
332 :
333 : /*
334 : * shut down subplans
335 : */
336 3197 : ExecEndNode(outerPlanState(node));
337 3197 : }
338 :
339 : void
340 39712 : ExecReScanProjectSet(ProjectSetState *node)
341 : {
276 tgl 342 GNC 39712 : PlanState *outerPlan = outerPlanState(node);
343 :
2272 andres 344 ECB : /* Forget any incompletely-evaluated SRFs */
2272 andres 345 GIC 39712 : node->pending_srf_tuples = false;
346 :
2272 andres 347 ECB : /*
348 : * If chgParam of subnode is not null then plan will be re-scanned by
349 : * first ExecProcNode.
350 : */
276 tgl 351 GNC 39712 : if (outerPlan->chgParam == NULL)
352 39712 : ExecReScan(outerPlan);
2272 andres 353 CBC 39712 : }
|