Fix a compiler warning in initStringInfo().
[pgsql.git] / src / backend / executor / nodeProjectSet.c
blob880f39fb2ff1e60b45c1415f6553150ad99f3d0c
1 /*-------------------------------------------------------------------------
3 * nodeProjectSet.c
4 * support for evaluating targetlists containing set-returning functions
6 * DESCRIPTION
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
17 * IDENTIFICATION
18 * src/backend/executor/nodeProjectSet.c
20 *-------------------------------------------------------------------------
23 #include "postgres.h"
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;
47 PlanState *outerPlan;
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)
71 return resultSlot;
75 * Get another input tuple and project SRFs from it.
77 for (;;)
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))
97 return NULL;
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.
113 if (resultSlot)
114 return resultSlot;
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);
123 return NULL;
126 /* ----------------------------------------------------------------
127 * ExecProjectSRF
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;
145 bool hasresult;
146 int argno;
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.
173 *result = (Datum) 0;
174 *isnull = true;
175 hassrf = true;
177 else if (IsA(elem, SetExprState))
180 * Evaluate SRF - possibly continuing previously started output.
182 *result = ExecMakeFunctionResultSet((SetExprState *) elem,
183 econtext, node->argcontext,
184 isnull, isdone);
186 if (*isdone != ExprEndResult)
187 hasresult = true;
188 if (*isdone == ExprMultipleResult)
189 node->pending_srf_tuples = true;
190 hassrf = true;
192 else
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 */
203 Assert(hassrf);
206 * If all the SRFs returned ExprEndResult, we consider that as no row
207 * being produced.
209 if (hasresult)
211 ExecStoreVirtualTuple(resultSlot);
212 return resultSlot;
215 return NULL;
218 /* ----------------------------------------------------------------
219 * ExecInitProjectSet
221 * Creates the run-time state information for the ProjectSet node
222 * produced by the planner and initializes outer relations
223 * (child nodes).
224 * ----------------------------------------------------------------
226 ProjectSetState *
227 ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
229 ProjectSetState *state;
230 ListCell *lc;
231 int off;
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.
281 off = 0;
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,
292 &state->ps);
294 else
296 Assert(!expression_returns_set((Node *) expr));
297 state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
300 off++;
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
312 * lifetimes.
314 state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
315 "tSRF function arguments",
316 ALLOCSET_DEFAULT_SIZES);
318 return state;
321 /* ----------------------------------------------------------------
322 * ExecEndProjectSet
324 * frees up storage allocated through C routines
325 * ----------------------------------------------------------------
327 void
328 ExecEndProjectSet(ProjectSetState *node)
331 * shut down subplans
333 ExecEndNode(outerPlanState(node));
336 void
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);