Age Owner Branch data 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-2025, 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 *
3074 andres@anarazel.de 42 :CBC 1414405 : ExecProjectSet(PlanState *pstate)
43 : : {
44 : 1414405 : ProjectSetState *node = castNode(ProjectSetState, pstate);
45 : : TupleTableSlot *outerTupleSlot;
46 : : TupleTableSlot *resultSlot;
47 : : PlanState *outerPlan;
48 : : ExprContext *econtext;
49 : :
3066 50 [ - + ]: 1414405 : CHECK_FOR_INTERRUPTS();
51 : :
3254 52 : 1414405 : 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 : : */
2991 59 : 1414405 : 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 : : */
3254 66 [ + + ]: 1414405 : if (node->pending_srf_tuples)
67 : : {
68 : 1399430 : resultSlot = ExecProjectSRF(node, true);
69 : :
70 [ + + ]: 1399430 : if (resultSlot != NULL)
71 : 1352677 : 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 : : */
780 tgl@sss.pgh.pa.us 88 : 101061 : MemoryContextReset(node->argcontext);
89 : :
90 : : /*
91 : : * Retrieve tuples from the outer plan until there are no more.
92 : : */
3254 andres@anarazel.de 93 : 101061 : outerPlan = outerPlanState(node);
94 : 101061 : outerTupleSlot = ExecProcNode(outerPlan);
95 : :
96 [ + + + + ]: 101061 : if (TupIsNull(outerTupleSlot))
97 : 14166 : return NULL;
98 : :
99 : : /*
100 : : * Prepare to compute projection expressions, which will expect to
101 : : * access the input tuples as varno OUTER.
102 : : */
103 : 86895 : econtext->ecxt_outertuple = outerTupleSlot;
104 : :
105 : : /* Evaluate the expressions */
106 : 86895 : 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 [ + + ]: 86140 : if (resultSlot)
114 : 46807 : 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 : : */
780 tgl@sss.pgh.pa.us 120 : 39333 : 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 *
3254 andres@anarazel.de 139 : 1486325 : ExecProjectSRF(ProjectSetState *node, bool continuing)
140 : : {
141 : 1486325 : TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
142 : 1486325 : 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 : 1486325 : ExecClearTuple(resultSlot);
149 : :
150 : : /* Call SRFs, as well as plain expressions, in per-tuple context */
2993 tgl@sss.pgh.pa.us 151 : 1486325 : 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 : : */
3254 andres@anarazel.de 157 : 1486325 : node->pending_srf_tuples = false;
158 : :
3199 159 : 1486325 : hassrf = hasresult = false;
160 [ + + ]: 4224610 : for (argno = 0; argno < node->nelems; argno++)
161 : : {
162 : 2739040 : Node *elem = node->elems[argno];
3254 163 : 2739040 : ExprDoneCond *isdone = &node->elemdone[argno];
164 : 2739040 : Datum *result = &resultSlot->tts_values[argno];
165 : 2739040 : bool *isnull = &resultSlot->tts_isnull[argno];
166 : :
167 [ + + + + ]: 2739040 : 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 : 15010 : *result = (Datum) 0;
174 : 15010 : *isnull = true;
175 : 15010 : hassrf = true;
176 : : }
3199 177 [ + + ]: 2724030 : else if (IsA(elem, SetExprState))
178 : : {
179 : : /*
180 : : * Evaluate SRF - possibly continuing previously started output.
181 : : */
182 : 1571949 : *result = ExecMakeFunctionResultSet((SetExprState *) elem,
183 : : econtext, node->argcontext,
184 : : isnull, isdone);
185 : :
3254 186 [ + + ]: 1571194 : if (*isdone != ExprEndResult)
187 : 1482211 : hasresult = true;
188 [ + + ]: 1571194 : if (*isdone == ExprMultipleResult)
189 : 1482208 : node->pending_srf_tuples = true;
190 : 1571194 : hassrf = true;
191 : : }
192 : : else
193 : : {
194 : : /* Non-SRF tlist expression, just evaluate normally. */
3199 195 : 1152081 : *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
3254 196 : 1152081 : *isdone = ExprSingleResult;
197 : : }
198 : : }
199 : :
2993 tgl@sss.pgh.pa.us 200 : 1485570 : MemoryContextSwitchTo(oldcontext);
201 : :
202 : : /* ProjectSet should not be used if there's no SRFs */
3254 andres@anarazel.de 203 [ - + ]: 1485570 : Assert(hassrf);
204 : :
205 : : /*
206 : : * If all the SRFs returned ExprEndResult, we consider that as no row
207 : : * being produced.
208 : : */
209 [ + + ]: 1485570 : if (hasresult)
210 : : {
211 : 1399484 : ExecStoreVirtualTuple(resultSlot);
212 : 1399484 : return resultSlot;
213 : : }
214 : :
215 : 86086 : 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 : 7088 : ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
228 : : {
229 : : ProjectSetState *state;
230 : : ListCell *lc;
231 : : int off;
232 : :
233 : : /* check for unsupported flags */
234 [ - + ]: 7088 : Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
235 : :
236 : : /*
237 : : * create state structure
238 : : */
239 : 7088 : state = makeNode(ProjectSetState);
240 : 7088 : state->ps.plan = (Plan *) node;
241 : 7088 : state->ps.state = estate;
3074 242 : 7088 : state->ps.ExecProcNode = ExecProjectSet;
243 : :
3254 244 : 7088 : state->pending_srf_tuples = false;
245 : :
246 : : /*
247 : : * Miscellaneous initialization
248 : : *
249 : : * create expression context for node
250 : : */
251 : 7088 : ExecAssignExprContext(estate, &state->ps);
252 : :
253 : : /*
254 : : * initialize child nodes
255 : : */
256 : 7088 : outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
257 : :
258 : : /*
259 : : * we don't use inner plan
260 : : */
261 [ - + ]: 7088 : Assert(innerPlan(node) == NULL);
262 : :
263 : : /*
264 : : * tuple table and result type initialization
265 : : */
2588 266 : 7088 : ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual);
267 : :
268 : : /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
3254 269 : 7088 : state->nelems = list_length(node->plan.targetlist);
6 michael@paquier.xyz 270 :GNC 7088 : state->elems = palloc_array(Node *, state->nelems);
271 : 7088 : 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 : : */
3199 andres@anarazel.de 279 :CBC 7088 : off = 0;
280 [ + - + + : 15111 : foreach(lc, node->plan.targetlist)
+ + ]
281 : : {
282 : 8024 : TargetEntry *te = (TargetEntry *) lfirst(lc);
283 : 8024 : Expr *expr = te->expr;
284 : :
2040 tgl@sss.pgh.pa.us 285 [ + + + + ]: 8024 : if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
286 [ + + + - ]: 853 : (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
287 : : {
3199 andres@anarazel.de 288 : 7173 : state->elems[off] = (Node *)
289 : 7174 : ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
290 : : &state->ps);
291 : : }
292 : : else
293 : : {
294 [ - + ]: 850 : Assert(!expression_returns_set((Node *) expr));
295 : 850 : state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
296 : : }
297 : :
298 : 8023 : off++;
299 : : }
300 : :
301 : : /* We don't support any qual on ProjectSet nodes */
2860 302 [ - + ]: 7087 : 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 : : */
2991 312 : 7087 : state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
313 : : "tSRF function arguments",
314 : : ALLOCSET_DEFAULT_SIZES);
315 : :
3254 316 : 7087 : return state;
317 : : }
318 : :
319 : : /* ----------------------------------------------------------------
320 : : * ExecEndProjectSet
321 : : *
322 : : * frees up storage allocated through C routines
323 : : * ----------------------------------------------------------------
324 : : */
325 : : void
326 : 6328 : ExecEndProjectSet(ProjectSetState *node)
327 : : {
328 : : /*
329 : : * shut down subplans
330 : : */
331 : 6328 : ExecEndNode(outerPlanState(node));
332 : 6328 : }
333 : :
334 : : void
335 : 9101 : ExecReScanProjectSet(ProjectSetState *node)
336 : : {
1258 tgl@sss.pgh.pa.us 337 : 9101 : PlanState *outerPlan = outerPlanState(node);
338 : :
339 : : /* Forget any incompletely-evaluated SRFs */
3254 andres@anarazel.de 340 : 9101 : 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 : : */
1258 tgl@sss.pgh.pa.us 346 [ + - ]: 9101 : if (outerPlan->chgParam == NULL)
347 : 9101 : ExecReScan(outerPlan);
3254 andres@anarazel.de 348 : 9101 : }
|