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