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-2022, 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"
29 #include "utils/memutils.h"
32 static TupleTableSlot
*ExecProjectSRF(ProjectSetState
*node
, bool continuing
);
35 /* ----------------------------------------------------------------
36 * ExecProjectSet(node)
38 * Return tuples after evaluating the targetlist (which contains set
39 * returning functions).
40 * ----------------------------------------------------------------
42 static TupleTableSlot
*
43 ExecProjectSet(PlanState
*pstate
)
45 ProjectSetState
*node
= castNode(ProjectSetState
, pstate
);
46 TupleTableSlot
*outerTupleSlot
;
47 TupleTableSlot
*resultSlot
;
49 ExprContext
*econtext
;
51 CHECK_FOR_INTERRUPTS();
53 econtext
= node
->ps
.ps_ExprContext
;
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.
60 ResetExprContext(econtext
);
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.
67 if (node
->pending_srf_tuples
)
69 resultSlot
= ExecProjectSRF(node
, true);
71 if (resultSlot
!= NULL
)
76 * Reset argument context to free any expression evaluation storage
77 * allocated in the previous tuple cycle. Note this can't happen until
78 * we're done projecting out tuples from a scan tuple, as ValuePerCall
79 * functions are allowed to reference the arguments for each returned
82 MemoryContextReset(node
->argcontext
);
85 * Get another input tuple and project SRFs from it.
90 * Retrieve tuples from the outer plan until there are no more.
92 outerPlan
= outerPlanState(node
);
93 outerTupleSlot
= ExecProcNode(outerPlan
);
95 if (TupIsNull(outerTupleSlot
))
99 * Prepare to compute projection expressions, which will expect to
100 * access the input tuples as varno OUTER.
102 econtext
->ecxt_outertuple
= outerTupleSlot
;
104 /* Evaluate the expressions */
105 resultSlot
= ExecProjectSRF(node
, false);
108 * Return the tuple unless the projection produced no rows (due to an
109 * empty set), in which case we must loop back to see if there are
110 * more outerPlan tuples.
119 /* ----------------------------------------------------------------
122 * Project a targetlist containing one or more set-returning functions.
124 * 'continuing' indicates whether to continue projecting rows for the
125 * same input tuple; or whether a new input tuple is being projected.
127 * Returns NULL if no output tuple has been produced.
129 * ----------------------------------------------------------------
131 static TupleTableSlot
*
132 ExecProjectSRF(ProjectSetState
*node
, bool continuing
)
134 TupleTableSlot
*resultSlot
= node
->ps
.ps_ResultTupleSlot
;
135 ExprContext
*econtext
= node
->ps
.ps_ExprContext
;
136 MemoryContext oldcontext
;
137 bool hassrf PG_USED_FOR_ASSERTS_ONLY
;
141 ExecClearTuple(resultSlot
);
143 /* Call SRFs, as well as plain expressions, in per-tuple context */
144 oldcontext
= MemoryContextSwitchTo(econtext
->ecxt_per_tuple_memory
);
147 * Assume no further tuples are produced unless an ExprMultipleResult is
148 * encountered from a set returning function.
150 node
->pending_srf_tuples
= false;
152 hassrf
= hasresult
= false;
153 for (argno
= 0; argno
< node
->nelems
; argno
++)
155 Node
*elem
= node
->elems
[argno
];
156 ExprDoneCond
*isdone
= &node
->elemdone
[argno
];
157 Datum
*result
= &resultSlot
->tts_values
[argno
];
158 bool *isnull
= &resultSlot
->tts_isnull
[argno
];
160 if (continuing
&& *isdone
== ExprEndResult
)
163 * If we're continuing to project output rows from a source tuple,
164 * return NULLs once the SRF has been exhausted.
170 else if (IsA(elem
, SetExprState
))
173 * Evaluate SRF - possibly continuing previously started output.
175 *result
= ExecMakeFunctionResultSet((SetExprState
*) elem
,
176 econtext
, node
->argcontext
,
179 if (*isdone
!= ExprEndResult
)
181 if (*isdone
== ExprMultipleResult
)
182 node
->pending_srf_tuples
= true;
187 /* Non-SRF tlist expression, just evaluate normally. */
188 *result
= ExecEvalExpr((ExprState
*) elem
, econtext
, isnull
);
189 *isdone
= ExprSingleResult
;
193 MemoryContextSwitchTo(oldcontext
);
195 /* ProjectSet should not be used if there's no SRFs */
199 * If all the SRFs returned ExprEndResult, we consider that as no row
204 ExecStoreVirtualTuple(resultSlot
);
211 /* ----------------------------------------------------------------
214 * Creates the run-time state information for the ProjectSet node
215 * produced by the planner and initializes outer relations
217 * ----------------------------------------------------------------
220 ExecInitProjectSet(ProjectSet
*node
, EState
*estate
, int eflags
)
222 ProjectSetState
*state
;
226 /* check for unsupported flags */
227 Assert(!(eflags
& (EXEC_FLAG_MARK
| EXEC_FLAG_BACKWARD
)));
230 * create state structure
232 state
= makeNode(ProjectSetState
);
233 state
->ps
.plan
= (Plan
*) node
;
234 state
->ps
.state
= estate
;
235 state
->ps
.ExecProcNode
= ExecProjectSet
;
237 state
->pending_srf_tuples
= false;
240 * Miscellaneous initialization
242 * create expression context for node
244 ExecAssignExprContext(estate
, &state
->ps
);
247 * initialize child nodes
249 outerPlanState(state
) = ExecInitNode(outerPlan(node
), estate
, eflags
);
252 * we don't use inner plan
254 Assert(innerPlan(node
) == NULL
);
257 * tuple table and result type initialization
259 ExecInitResultTupleSlotTL(&state
->ps
, &TTSOpsVirtual
);
261 /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
262 state
->nelems
= list_length(node
->plan
.targetlist
);
263 state
->elems
= (Node
**)
264 palloc(sizeof(Node
*) * state
->nelems
);
265 state
->elemdone
= (ExprDoneCond
*)
266 palloc(sizeof(ExprDoneCond
) * state
->nelems
);
269 * Build expressions to evaluate targetlist. We can't use
270 * ExecBuildProjectionInfo here, since that doesn't deal with SRFs.
271 * Instead compile each expression separately, using
272 * ExecInitFunctionResultSet where applicable.
275 foreach(lc
, node
->plan
.targetlist
)
277 TargetEntry
*te
= (TargetEntry
*) lfirst(lc
);
278 Expr
*expr
= te
->expr
;
280 if ((IsA(expr
, FuncExpr
) && ((FuncExpr
*) expr
)->funcretset
) ||
281 (IsA(expr
, OpExpr
) && ((OpExpr
*) expr
)->opretset
))
283 state
->elems
[off
] = (Node
*)
284 ExecInitFunctionResultSet(expr
, state
->ps
.ps_ExprContext
,
289 Assert(!expression_returns_set((Node
*) expr
));
290 state
->elems
[off
] = (Node
*) ExecInitExpr(expr
, &state
->ps
);
296 /* We don't support any qual on ProjectSet nodes */
297 Assert(node
->plan
.qual
== NIL
);
300 * Create a memory context that ExecMakeFunctionResultSet can use to
301 * evaluate function arguments in. We can't use the per-tuple context for
302 * this because it gets reset too often; but we don't want to leak
303 * evaluation results into the query-lifespan context either. We use one
304 * context for the arguments of all tSRFs, as they have roughly equivalent
307 state
->argcontext
= AllocSetContextCreate(CurrentMemoryContext
,
308 "tSRF function arguments",
309 ALLOCSET_DEFAULT_SIZES
);
314 /* ----------------------------------------------------------------
317 * frees up storage allocated through C routines
318 * ----------------------------------------------------------------
321 ExecEndProjectSet(ProjectSetState
*node
)
324 * Free the exprcontext
326 ExecFreeExprContext(&node
->ps
);
329 * clean out the tuple table
331 ExecClearTuple(node
->ps
.ps_ResultTupleSlot
);
336 ExecEndNode(outerPlanState(node
));
340 ExecReScanProjectSet(ProjectSetState
*node
)
342 PlanState
*outerPlan
= outerPlanState(node
);
344 /* Forget any incompletely-evaluated SRFs */
345 node
->pending_srf_tuples
= false;
348 * If chgParam of subnode is not null then plan will be re-scanned by
349 * first ExecProcNode.
351 if (outerPlan
->chgParam
== NULL
)
352 ExecReScan(outerPlan
);