PostgreSQL Source Code  git master
nodeProjectSet.c
Go to the documentation of this file.
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"
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 *
43 {
44  ProjectSetState *node = castNode(ProjectSetState, pstate);
45  TupleTableSlot *outerTupleSlot;
46  TupleTableSlot *resultSlot;
48  ExprContext *econtext;
49 
51 
52  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  */
59  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  */
66  if (node->pending_srf_tuples)
67  {
68  resultSlot = ExecProjectSRF(node, true);
69 
70  if (resultSlot != NULL)
71  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  */
89 
90  /*
91  * Retrieve tuples from the outer plan until there are no more.
92  */
93  outerPlan = outerPlanState(node);
94  outerTupleSlot = ExecProcNode(outerPlan);
95 
96  if (TupIsNull(outerTupleSlot))
97  return NULL;
98 
99  /*
100  * Prepare to compute projection expressions, which will expect to
101  * access the input tuples as varno OUTER.
102  */
103  econtext->ecxt_outertuple = outerTupleSlot;
104 
105  /* Evaluate the expressions */
106  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  if (resultSlot)
114  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  */
120  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 *
139 ExecProjectSRF(ProjectSetState *node, bool continuing)
140 {
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;
147 
148  ExecClearTuple(resultSlot);
149 
150  /* Call SRFs, as well as plain expressions, in per-tuple context */
151  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  */
157  node->pending_srf_tuples = false;
158 
159  hassrf = hasresult = false;
160  for (argno = 0; argno < node->nelems; argno++)
161  {
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];
166 
167  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  *result = (Datum) 0;
174  *isnull = true;
175  hassrf = true;
176  }
177  else if (IsA(elem, SetExprState))
178  {
179  /*
180  * Evaluate SRF - possibly continuing previously started output.
181  */
182  *result = ExecMakeFunctionResultSet((SetExprState *) elem,
183  econtext, node->argcontext,
184  isnull, isdone);
185 
186  if (*isdone != ExprEndResult)
187  hasresult = true;
188  if (*isdone == ExprMultipleResult)
189  node->pending_srf_tuples = true;
190  hassrf = true;
191  }
192  else
193  {
194  /* Non-SRF tlist expression, just evaluate normally. */
195  *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
196  *isdone = ExprSingleResult;
197  }
198  }
199 
200  MemoryContextSwitchTo(oldcontext);
201 
202  /* ProjectSet should not be used if there's no SRFs */
203  Assert(hassrf);
204 
205  /*
206  * If all the SRFs returned ExprEndResult, we consider that as no row
207  * being produced.
208  */
209  if (hasresult)
210  {
211  ExecStoreVirtualTuple(resultSlot);
212  return resultSlot;
213  }
214 
215  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  */
227 ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
228 {
230  ListCell *lc;
231  int off;
232 
233  /* check for unsupported flags */
234  Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
235 
236  /*
237  * create state structure
238  */
240  state->ps.plan = (Plan *) node;
241  state->ps.state = estate;
242  state->ps.ExecProcNode = ExecProjectSet;
243 
244  state->pending_srf_tuples = false;
245 
246  /*
247  * Miscellaneous initialization
248  *
249  * create expression context for node
250  */
251  ExecAssignExprContext(estate, &state->ps);
252 
253  /*
254  * initialize child nodes
255  */
256  outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
257 
258  /*
259  * we don't use inner plan
260  */
261  Assert(innerPlan(node) == NULL);
262 
263  /*
264  * tuple table and result type initialization
265  */
267 
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);
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  */
281  off = 0;
282  foreach(lc, node->plan.targetlist)
283  {
284  TargetEntry *te = (TargetEntry *) lfirst(lc);
285  Expr *expr = te->expr;
286 
287  if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
288  (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
289  {
290  state->elems[off] = (Node *)
291  ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
292  &state->ps);
293  }
294  else
295  {
296  Assert(!expression_returns_set((Node *) expr));
297  state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
298  }
299 
300  off++;
301  }
302 
303  /* We don't support any qual on ProjectSet nodes */
304  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  */
315  "tSRF function arguments",
317 
318  return state;
319 }
320 
321 /* ----------------------------------------------------------------
322  * ExecEndProjectSet
323  *
324  * frees up storage allocated through C routines
325  * ----------------------------------------------------------------
326  */
327 void
329 {
330  /*
331  * shut down subplans
332  */
334 }
335 
336 void
338 {
340 
341  /* Forget any incompletely-evaluated SRFs */
342  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  */
348  if (outerPlan->chgParam == NULL)
350 }
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:182
#define Assert(condition)
Definition: c.h:858
void ExecReScan(PlanState *node)
Definition: execAmi.c:76
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:134
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:557
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:142
Datum ExecMakeFunctionResultSet(SetExprState *fcache, ExprContext *econtext, MemoryContext argContext, bool *isNull, ExprDoneCond *isDone)
Definition: execSRF.c:497
SetExprState * ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent)
Definition: execSRF.c:444
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:84
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1639
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1886
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:483
#define outerPlanState(node)
Definition: execnodes.h:1214
ExprDoneCond
Definition: execnodes.h:302
@ ExprSingleResult
Definition: execnodes.h:303
@ ExprMultipleResult
Definition: execnodes.h:304
@ ExprEndResult
Definition: execnodes.h:305
#define EXEC_FLAG_BACKWARD
Definition: executor.h:68
#define ResetExprContext(econtext)
Definition: executor.h:544
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:333
#define EXEC_FLAG_MARK
Definition: executor.h:69
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:269
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:383
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void * palloc(Size size)
Definition: mcxt.c:1316
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:160
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:122
bool expression_returns_set(Node *clause)
Definition: nodeFuncs.c:758
static TupleTableSlot * ExecProjectSet(PlanState *pstate)
void ExecReScanProjectSet(ProjectSetState *node)
ProjectSetState * ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
void ExecEndProjectSet(ProjectSetState *node)
static TupleTableSlot * ExecProjectSRF(ProjectSetState *node, bool continuing)
#define IsA(nodeptr, _type_)
Definition: nodes.h:158
#define makeNode(_type_)
Definition: nodes.h:155
#define castNode(_type_, nodeptr)
Definition: nodes.h:176
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define innerPlan(node)
Definition: plannodes.h:181
#define outerPlan(node)
Definition: plannodes.h:182
uintptr_t Datum
Definition: postgres.h:64
MemoryContextSwitchTo(old_ctx)
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:263
TupleTableSlot * ecxt_outertuple
Definition: execnodes.h:259
Definition: nodes.h:129
ExprContext * ps_ExprContext
Definition: execnodes.h:1157
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1156
List * qual
Definition: plannodes.h:153
List * targetlist
Definition: plannodes.h:152
PlanState ps
Definition: execnodes.h:1336
MemoryContext argcontext
Definition: execnodes.h:1341
bool pending_srf_tuples
Definition: execnodes.h:1340
ExprDoneCond * elemdone
Definition: execnodes.h:1338
Plan plan
Definition: plannodes.h:210
Expr * expr
Definition: primnodes.h:2162
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
Definition: regguts.h:323
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
#define TupIsNull(slot)
Definition: tuptable.h:306