1 /*-------------------------------------------------------------------------
4 * support for evaluating targetlists containing set-returning functions
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.
14 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
15 * Portions Copyright (c) 1994, Regents of the University of California
18 * src/backend/executor/nodeProjectSet.c
20 *-------------------------------------------------------------------------
25 #include "executor/executor.h"
26 #include "executor/nodeProjectSet.h"
27 #include "miscadmin.h"
28 #include "nodes/nodeFuncs.h"
31 static TupleTableSlot
*ExecProjectSRF(ProjectSetState
*node
, bool continuing
);
34 /* ----------------------------------------------------------------
35 * ExecProjectSet(node)
37 * Return tuples after evaluating the targetlist (which contains set
38 * returning functions).
39 * ----------------------------------------------------------------
41 static TupleTableSlot
*
42 ExecProjectSet(PlanState
*pstate
)
44 ProjectSetState
*node
= castNode(ProjectSetState
, pstate
);
45 TupleTableSlot
*outerTupleSlot
;
46 TupleTableSlot
*resultSlot
;
48 ExprContext
*econtext
;
50 CHECK_FOR_INTERRUPTS();
52 econtext
= node
->ps
.ps_ExprContext
;
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.
59 ResetExprContext(econtext
);
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.
66 if (node
->pending_srf_tuples
)
68 resultSlot
= ExecProjectSRF(node
, true);
70 if (resultSlot
!= NULL
)
75 * Get another input tuple and project SRFs from it.
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.
88 MemoryContextReset(node
->argcontext
);
91 * Retrieve tuples from the outer plan until there are no more.
93 outerPlan
= outerPlanState(node
);
94 outerTupleSlot
= ExecProcNode(outerPlan
);
96 if (TupIsNull(outerTupleSlot
))
100 * Prepare to compute projection expressions, which will expect to
101 * access the input tuples as varno OUTER.
103 econtext
->ecxt_outertuple
= outerTupleSlot
;
105 /* Evaluate the expressions */
106 resultSlot
= ExecProjectSRF(node
, false);
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.
117 * When we do loop back, we'd better reset the econtext again, just in
118 * case the SRF leaked some memory there.
120 ResetExprContext(econtext
);
126 /* ----------------------------------------------------------------
129 * Project a targetlist containing one or more set-returning functions.
131 * 'continuing' indicates whether to continue projecting rows for the
132 * same input tuple; or whether a new input tuple is being projected.
134 * Returns NULL if no output tuple has been produced.
136 * ----------------------------------------------------------------
138 static TupleTableSlot
*
139 ExecProjectSRF(ProjectSetState
*node
, bool continuing
)
141 TupleTableSlot
*resultSlot
= node
->ps
.ps_ResultTupleSlot
;
142 ExprContext
*econtext
= node
->ps
.ps_ExprContext
;
143 MemoryContext oldcontext
;
144 bool hassrf PG_USED_FOR_ASSERTS_ONLY
;
148 ExecClearTuple(resultSlot
);
150 /* Call SRFs, as well as plain expressions, in per-tuple context */
151 oldcontext
= MemoryContextSwitchTo(econtext
->ecxt_per_tuple_memory
);
154 * Assume no further tuples are produced unless an ExprMultipleResult is
155 * encountered from a set returning function.
157 node
->pending_srf_tuples
= false;
159 hassrf
= hasresult
= false;
160 for (argno
= 0; argno
< node
->nelems
; argno
++)
162 Node
*elem
= node
->elems
[argno
];
163 ExprDoneCond
*isdone
= &node
->elemdone
[argno
];
164 Datum
*result
= &resultSlot
->tts_values
[argno
];
165 bool *isnull
= &resultSlot
->tts_isnull
[argno
];
167 if (continuing
&& *isdone
== ExprEndResult
)
170 * If we're continuing to project output rows from a source tuple,
171 * return NULLs once the SRF has been exhausted.
177 else if (IsA(elem
, SetExprState
))
180 * Evaluate SRF - possibly continuing previously started output.
182 *result
= ExecMakeFunctionResultSet((SetExprState
*) elem
,
183 econtext
, node
->argcontext
,
186 if (*isdone
!= ExprEndResult
)
188 if (*isdone
== ExprMultipleResult
)
189 node
->pending_srf_tuples
= true;
194 /* Non-SRF tlist expression, just evaluate normally. */
195 *result
= ExecEvalExpr((ExprState
*) elem
, econtext
, isnull
);
196 *isdone
= ExprSingleResult
;
200 MemoryContextSwitchTo(oldcontext
);
202 /* ProjectSet should not be used if there's no SRFs */
206 * If all the SRFs returned ExprEndResult, we consider that as no row
211 ExecStoreVirtualTuple(resultSlot
);
218 /* ----------------------------------------------------------------
221 * Creates the run-time state information for the ProjectSet node
222 * produced by the planner and initializes outer relations
224 * ----------------------------------------------------------------
227 ExecInitProjectSet(ProjectSet
*node
, EState
*estate
, int eflags
)
229 ProjectSetState
*state
;
233 /* check for unsupported flags */
234 Assert(!(eflags
& (EXEC_FLAG_MARK
| EXEC_FLAG_BACKWARD
)));
237 * create state structure
239 state
= makeNode(ProjectSetState
);
240 state
->ps
.plan
= (Plan
*) node
;
241 state
->ps
.state
= estate
;
242 state
->ps
.ExecProcNode
= ExecProjectSet
;
244 state
->pending_srf_tuples
= false;
247 * Miscellaneous initialization
249 * create expression context for node
251 ExecAssignExprContext(estate
, &state
->ps
);
254 * initialize child nodes
256 outerPlanState(state
) = ExecInitNode(outerPlan(node
), estate
, eflags
);
259 * we don't use inner plan
261 Assert(innerPlan(node
) == NULL
);
264 * tuple table and result type initialization
266 ExecInitResultTupleSlotTL(&state
->ps
, &TTSOpsVirtual
);
268 /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
269 state
->nelems
= list_length(node
->plan
.targetlist
);
270 state
->elems
= (Node
**)
271 palloc(sizeof(Node
*) * state
->nelems
);
272 state
->elemdone
= (ExprDoneCond
*)
273 palloc(sizeof(ExprDoneCond
) * state
->nelems
);
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.
282 foreach(lc
, node
->plan
.targetlist
)
284 TargetEntry
*te
= (TargetEntry
*) lfirst(lc
);
285 Expr
*expr
= te
->expr
;
287 if ((IsA(expr
, FuncExpr
) && ((FuncExpr
*) expr
)->funcretset
) ||
288 (IsA(expr
, OpExpr
) && ((OpExpr
*) expr
)->opretset
))
290 state
->elems
[off
] = (Node
*)
291 ExecInitFunctionResultSet(expr
, state
->ps
.ps_ExprContext
,
296 Assert(!expression_returns_set((Node
*) expr
));
297 state
->elems
[off
] = (Node
*) ExecInitExpr(expr
, &state
->ps
);
303 /* We don't support any qual on ProjectSet nodes */
304 Assert(node
->plan
.qual
== NIL
);
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
314 state
->argcontext
= AllocSetContextCreate(CurrentMemoryContext
,
315 "tSRF function arguments",
316 ALLOCSET_DEFAULT_SIZES
);
321 /* ----------------------------------------------------------------
324 * frees up storage allocated through C routines
325 * ----------------------------------------------------------------
328 ExecEndProjectSet(ProjectSetState
*node
)
333 ExecEndNode(outerPlanState(node
));
337 ExecReScanProjectSet(ProjectSetState
*node
)
339 PlanState
*outerPlan
= outerPlanState(node
);
341 /* Forget any incompletely-evaluated SRFs */
342 node
->pending_srf_tuples
= false;
345 * If chgParam of subnode is not null then plan will be re-scanned by
346 * first ExecProcNode.
348 if (outerPlan
->chgParam
== NULL
)
349 ExecReScan(outerPlan
);