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