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-2024, 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 *
2463 andres@anarazel.de 42 :CBC 998476 : ExecProjectSet(PlanState *pstate)
43 : : {
44 : 998476 : ProjectSetState *node = castNode(ProjectSetState, pstate);
45 : : TupleTableSlot *outerTupleSlot;
46 : : TupleTableSlot *resultSlot;
47 : : PlanState *outerPlan;
48 : : ExprContext *econtext;
49 : :
2455 50 [ - + ]: 998476 : CHECK_FOR_INTERRUPTS();
51 : :
2643 52 : 998476 : 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 : : */
2380 59 : 998476 : 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 : : */
2643 66 [ + + ]: 998476 : if (node->pending_srf_tuples)
67 : : {
68 : 988316 : resultSlot = ExecProjectSRF(node, true);
69 : :
70 [ + + ]: 988316 : if (resultSlot != NULL)
71 : 949851 : 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 : : */
169 tgl@sss.pgh.pa.us 88 : 80561 : MemoryContextReset(node->argcontext);
89 : :
90 : : /*
91 : : * Retrieve tuples from the outer plan until there are no more.
92 : : */
2643 andres@anarazel.de 93 : 80561 : outerPlan = outerPlanState(node);
94 : 80561 : outerTupleSlot = ExecProcNode(outerPlan);
95 : :
96 [ + + + + ]: 80561 : if (TupIsNull(outerTupleSlot))
97 : 9358 : return NULL;
98 : :
99 : : /*
100 : : * Prepare to compute projection expressions, which will expect to
101 : : * access the input tuples as varno OUTER.
102 : : */
103 : 71203 : econtext->ecxt_outertuple = outerTupleSlot;
104 : :
105 : : /* Evaluate the expressions */
106 : 71203 : 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 [ + + ]: 70452 : if (resultSlot)
114 : 38516 : 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 : : */
169 tgl@sss.pgh.pa.us 120 : 31936 : 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 *
2643 andres@anarazel.de 139 : 1059519 : ExecProjectSRF(ProjectSetState *node, bool continuing)
140 : : {
141 : 1059519 : TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
142 : 1059519 : 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 : 1059519 : ExecClearTuple(resultSlot);
149 : :
150 : : /* Call SRFs, as well as plain expressions, in per-tuple context */
2382 tgl@sss.pgh.pa.us 151 : 1059519 : 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 : : */
2643 andres@anarazel.de 157 : 1059519 : node->pending_srf_tuples = false;
158 : :
2588 159 : 1059519 : hassrf = hasresult = false;
160 [ + + ]: 2992295 : for (argno = 0; argno < node->nelems; argno++)
161 : : {
162 : 1933527 : Node *elem = node->elems[argno];
2643 163 : 1933527 : ExprDoneCond *isdone = &node->elemdone[argno];
164 : 1933527 : Datum *result = &resultSlot->tts_values[argno];
165 : 1933527 : bool *isnull = &resultSlot->tts_isnull[argno];
166 : :
167 [ + + + + ]: 1933527 : 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 : : }
2588 177 [ + + ]: 1918517 : else if (IsA(elem, SetExprState))
178 : : {
179 : : /*
180 : : * Evaluate SRF - possibly continuing previously started output.
181 : : */
182 : 1145165 : *result = ExecMakeFunctionResultSet((SetExprState *) elem,
183 : : econtext, node->argcontext,
184 : : isnull, isdone);
185 : :
2643 186 [ + + ]: 1144414 : if (*isdone != ExprEndResult)
187 : 1071108 : hasresult = true;
188 [ + + ]: 1144414 : if (*isdone == ExprMultipleResult)
189 : 1071105 : node->pending_srf_tuples = true;
190 : 1144414 : hassrf = true;
191 : : }
192 : : else
193 : : {
194 : : /* Non-SRF tlist expression, just evaluate normally. */
2588 195 : 773352 : *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
2643 196 : 773352 : *isdone = ExprSingleResult;
197 : : }
198 : : }
199 : :
2382 tgl@sss.pgh.pa.us 200 : 1058768 : MemoryContextSwitchTo(oldcontext);
201 : :
202 : : /* ProjectSet should not be used if there's no SRFs */
2643 andres@anarazel.de 203 [ - + ]: 1058768 : Assert(hassrf);
204 : :
205 : : /*
206 : : * If all the SRFs returned ExprEndResult, we consider that as no row
207 : : * being produced.
208 : : */
209 [ + + ]: 1058768 : if (hasresult)
210 : : {
211 : 988367 : ExecStoreVirtualTuple(resultSlot);
212 : 988367 : return resultSlot;
213 : : }
214 : :
215 : 70401 : 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 : 4458 : ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
228 : : {
229 : : ProjectSetState *state;
230 : : ListCell *lc;
231 : : int off;
232 : :
233 : : /* check for unsupported flags */
234 [ - + ]: 4458 : Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
235 : :
236 : : /*
237 : : * create state structure
238 : : */
239 : 4458 : state = makeNode(ProjectSetState);
240 : 4458 : state->ps.plan = (Plan *) node;
241 : 4458 : state->ps.state = estate;
2463 242 : 4458 : state->ps.ExecProcNode = ExecProjectSet;
243 : :
2643 244 : 4458 : state->pending_srf_tuples = false;
245 : :
246 : : /*
247 : : * Miscellaneous initialization
248 : : *
249 : : * create expression context for node
250 : : */
251 : 4458 : ExecAssignExprContext(estate, &state->ps);
252 : :
253 : : /*
254 : : * initialize child nodes
255 : : */
256 : 4458 : outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
257 : :
258 : : /*
259 : : * we don't use inner plan
260 : : */
261 [ - + ]: 4458 : Assert(innerPlan(node) == NULL);
262 : :
263 : : /*
264 : : * tuple table and result type initialization
265 : : */
1977 266 : 4458 : ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual);
267 : :
268 : : /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
2643 269 : 4458 : state->nelems = list_length(node->plan.targetlist);
2588 270 : 4458 : state->elems = (Node **)
271 : 4458 : palloc(sizeof(Node *) * state->nelems);
2643 272 : 4458 : state->elemdone = (ExprDoneCond *)
273 : 4458 : palloc(sizeof(ExprDoneCond) * state->nelems);
274 : :
275 : : /*
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.
280 : : */
2588 281 : 4458 : off = 0;
282 [ + - + + : 9776 : foreach(lc, node->plan.targetlist)
+ + ]
283 : : {
284 : 5319 : TargetEntry *te = (TargetEntry *) lfirst(lc);
285 : 5319 : Expr *expr = te->expr;
286 : :
1429 tgl@sss.pgh.pa.us 287 [ + + + + ]: 5319 : if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
288 [ + + + - ]: 785 : (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
289 : : {
2588 andres@anarazel.de 290 : 4536 : state->elems[off] = (Node *)
291 : 4537 : ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
292 : : &state->ps);
293 : : }
294 : : else
295 : : {
296 [ - + ]: 782 : Assert(!expression_returns_set((Node *) expr));
297 : 782 : state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
298 : : }
299 : :
300 : 5318 : off++;
301 : : }
302 : :
303 : : /* We don't support any qual on ProjectSet nodes */
2249 304 [ - + ]: 4457 : Assert(node->plan.qual == NIL);
305 : :
306 : : /*
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.
313 : : */
2380 314 : 4457 : state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
315 : : "tSRF function arguments",
316 : : ALLOCSET_DEFAULT_SIZES);
317 : :
2643 318 : 4457 : return state;
319 : : }
320 : :
321 : : /* ----------------------------------------------------------------
322 : : * ExecEndProjectSet
323 : : *
324 : : * frees up storage allocated through C routines
325 : : * ----------------------------------------------------------------
326 : : */
327 : : void
328 : 3702 : ExecEndProjectSet(ProjectSetState *node)
329 : : {
330 : : /*
331 : : * shut down subplans
332 : : */
333 : 3702 : ExecEndNode(outerPlanState(node));
334 : 3702 : }
335 : :
336 : : void
337 : 6005 : ExecReScanProjectSet(ProjectSetState *node)
338 : : {
647 tgl@sss.pgh.pa.us 339 : 6005 : PlanState *outerPlan = outerPlanState(node);
340 : :
341 : : /* Forget any incompletely-evaluated SRFs */
2643 andres@anarazel.de 342 : 6005 : node->pending_srf_tuples = false;
343 : :
344 : : /*
345 : : * If chgParam of subnode is not null then plan will be re-scanned by
346 : : * first ExecProcNode.
347 : : */
647 tgl@sss.pgh.pa.us 348 [ + - ]: 6005 : if (outerPlan->chgParam == NULL)
349 : 6005 : ExecReScan(outerPlan);
2643 andres@anarazel.de 350 : 6005 : }
|