Branch data 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-2026, 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 : 178969 : ExecProjectSet(PlanState *pstate)
43 : : {
44 : 178969 : ProjectSetState *node = castNode(ProjectSetState, pstate);
45 : 178969 : TupleTableSlot *outerTupleSlot;
46 : 178969 : TupleTableSlot *resultSlot;
47 : 178969 : PlanState *outerPlan;
48 : 178969 : ExprContext *econtext;
49 : :
50 [ + - ]: 178969 : CHECK_FOR_INTERRUPTS();
51 : :
52 : 178969 : 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 : 178969 : 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 [ + + ]: 178969 : if (node->pending_srf_tuples)
67 : : {
68 : 173398 : resultSlot = ExecProjectSRF(node, true);
69 : :
70 [ + + ]: 173398 : if (resultSlot != NULL)
71 : 150291 : return resultSlot;
72 : 23107 : }
73 : :
74 : : /*
75 : : * Get another input tuple and project SRFs from it.
76 : : */
77 : 44593 : 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 : 44593 : MemoryContextReset(node->argcontext);
89 : :
90 : : /*
91 : : * Retrieve tuples from the outer plan until there are no more.
92 : : */
93 : 44593 : outerPlan = outerPlanState(node);
94 : 44593 : outerTupleSlot = ExecProcNode(outerPlan);
95 : :
96 [ + + + + ]: 44593 : if (TupIsNull(outerTupleSlot))
97 : 5552 : return NULL;
98 : :
99 : : /*
100 : : * Prepare to compute projection expressions, which will expect to
101 : : * access the input tuples as varno OUTER.
102 : : */
103 : 39041 : econtext->ecxt_outertuple = outerTupleSlot;
104 : :
105 : : /* Evaluate the expressions */
106 : 39041 : 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 [ + + ]: 39041 : if (resultSlot)
114 : 23126 : 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 : 15915 : ResetExprContext(econtext);
121 : : }
122 : :
123 : : return NULL;
124 : 178969 : }
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 : 212439 : ExecProjectSRF(ProjectSetState *node, bool continuing)
140 : : {
141 : 212439 : TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
142 : 212439 : ExprContext *econtext = node->ps.ps_ExprContext;
143 : 212439 : MemoryContext oldcontext;
144 : 212439 : bool hassrf PG_USED_FOR_ASSERTS_ONLY;
145 : 212439 : bool hasresult;
146 : 212439 : int argno;
147 : :
148 : 212439 : ExecClearTuple(resultSlot);
149 : :
150 : : /* Call SRFs, as well as plain expressions, in per-tuple context */
151 : 212439 : 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 : 212439 : node->pending_srf_tuples = false;
158 : :
159 : 212439 : hassrf = hasresult = false;
160 [ + + ]: 629060 : for (argno = 0; argno < node->nelems; argno++)
161 : : {
162 : 416621 : Node *elem = node->elems[argno];
163 : 416621 : ExprDoneCond *isdone = &node->elemdone[argno];
164 : 416621 : Datum *result = &resultSlot->tts_values[argno];
165 : 416621 : bool *isnull = &resultSlot->tts_isnull[argno];
166 : :
167 [ + + + + ]: 416621 : 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 : 5005 : *result = (Datum) 0;
174 : 5005 : *isnull = true;
175 : 5005 : hassrf = true;
176 : 5005 : }
177 [ + + ]: 411616 : else if (IsA(elem, SetExprState))
178 : : {
179 : : /*
180 : : * Evaluate SRF - possibly continuing previously started output.
181 : : */
182 : 479956 : *result = ExecMakeFunctionResultSet((SetExprState *) elem,
183 : 239978 : econtext, node->argcontext,
184 : 239978 : isnull, isdone);
185 : :
186 [ + + ]: 239978 : if (*isdone != ExprEndResult)
187 : 199990 : hasresult = true;
188 [ + + ]: 239978 : if (*isdone == ExprMultipleResult)
189 : 199990 : node->pending_srf_tuples = true;
190 : 239978 : hassrf = true;
191 : 239978 : }
192 : : else
193 : : {
194 : : /* Non-SRF tlist expression, just evaluate normally. */
195 : 171638 : *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
196 : 171638 : *isdone = ExprSingleResult;
197 : : }
198 : 416621 : }
199 : :
200 : 212439 : MemoryContextSwitchTo(oldcontext);
201 : :
202 : : /* ProjectSet should not be used if there's no SRFs */
203 [ + - ]: 212439 : Assert(hassrf);
204 : :
205 : : /*
206 : : * If all the SRFs returned ExprEndResult, we consider that as no row
207 : : * being produced.
208 : : */
209 [ + + ]: 212439 : if (hasresult)
210 : : {
211 : 173417 : ExecStoreVirtualTuple(resultSlot);
212 : 173417 : return resultSlot;
213 : : }
214 : :
215 : 39022 : return NULL;
216 : 212439 : }
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 : 2001 : ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
228 : : {
229 : 2001 : ProjectSetState *state;
230 : 2001 : ListCell *lc;
231 : 2001 : int off;
232 : :
233 : : /* check for unsupported flags */
234 [ + - ]: 2001 : Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
235 : :
236 : : /*
237 : : * create state structure
238 : : */
239 : 2001 : state = makeNode(ProjectSetState);
240 : 2001 : state->ps.plan = (Plan *) node;
241 : 2001 : state->ps.state = estate;
242 : 2001 : state->ps.ExecProcNode = ExecProjectSet;
243 : :
244 : 2001 : state->pending_srf_tuples = false;
245 : :
246 : : /*
247 : : * Miscellaneous initialization
248 : : *
249 : : * create expression context for node
250 : : */
251 : 2001 : ExecAssignExprContext(estate, &state->ps);
252 : :
253 : : /*
254 : : * initialize child nodes
255 : : */
256 : 2001 : outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
257 : :
258 : : /*
259 : : * we don't use inner plan
260 : : */
261 [ + - ]: 2001 : Assert(innerPlan(node) == NULL);
262 : :
263 : : /*
264 : : * tuple table and result type initialization
265 : : */
266 : 2001 : ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual);
267 : :
268 : : /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
269 : 2001 : state->nelems = list_length(node->plan.targetlist);
270 : 2001 : state->elems = palloc_array(Node *, state->nelems);
271 : 2001 : state->elemdone = palloc_array(ExprDoneCond, state->nelems);
272 : :
273 : : /*
274 : : * Build expressions to evaluate targetlist. We can't use
275 : : * ExecBuildProjectionInfo here, since that doesn't deal with SRFs.
276 : : * Instead compile each expression separately, using
277 : : * ExecInitFunctionResultSet where applicable.
278 : : */
279 : 2001 : off = 0;
280 [ + - + + : 4233 : foreach(lc, node->plan.targetlist)
+ + ]
281 : : {
282 : 2232 : TargetEntry *te = (TargetEntry *) lfirst(lc);
283 : 2232 : Expr *expr = te->expr;
284 : :
285 [ + + + - ]: 2233 : if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
286 [ + + ]: 203 : (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
287 : : {
288 : 2030 : state->elems[off] = (Node *)
289 : 4060 : ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
290 : 2030 : &state->ps);
291 : 2030 : }
292 : : else
293 : : {
294 [ + - ]: 202 : Assert(!expression_returns_set((Node *) expr));
295 : 202 : state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
296 : : }
297 : :
298 : 2232 : off++;
299 : 2232 : }
300 : :
301 : : /* We don't support any qual on ProjectSet nodes */
302 [ + - ]: 2001 : Assert(node->plan.qual == NIL);
303 : :
304 : : /*
305 : : * Create a memory context that ExecMakeFunctionResultSet can use to
306 : : * evaluate function arguments in. We can't use the per-tuple context for
307 : : * this because it gets reset too often; but we don't want to leak
308 : : * evaluation results into the query-lifespan context either. We use one
309 : : * context for the arguments of all tSRFs, as they have roughly equivalent
310 : : * lifetimes.
311 : : */
312 : 2001 : state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
313 : : "tSRF function arguments",
314 : : ALLOCSET_DEFAULT_SIZES);
315 : :
316 : 4002 : return state;
317 : 2001 : }
318 : :
319 : : /* ----------------------------------------------------------------
320 : : * ExecEndProjectSet
321 : : *
322 : : * frees up storage allocated through C routines
323 : : * ----------------------------------------------------------------
324 : : */
325 : : void
326 : 1762 : ExecEndProjectSet(ProjectSetState *node)
327 : : {
328 : : /*
329 : : * shut down subplans
330 : : */
331 : 1762 : ExecEndNode(outerPlanState(node));
332 : 1762 : }
333 : :
334 : : void
335 : 4360 : ExecReScanProjectSet(ProjectSetState *node)
336 : : {
337 : 4360 : PlanState *outerPlan = outerPlanState(node);
338 : :
339 : : /* Forget any incompletely-evaluated SRFs */
340 : 4360 : node->pending_srf_tuples = false;
341 : :
342 : : /*
343 : : * If chgParam of subnode is not null then plan will be re-scanned by
344 : : * first ExecProcNode.
345 : : */
346 [ - + ]: 4360 : if (outerPlan->chgParam == NULL)
347 : 4360 : ExecReScan(outerPlan);
348 : 4360 : }
|