PostgreSQL Source Code  git master
execSRF.c File Reference
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_proc.h"
#include "executor/execdebug.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "pgstat.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
Include dependency graph for execSRF.c:

Go to the source code of this file.

Functions

static void init_sexpr (Oid foid, Oid input_collation, Expr *node, SetExprState *sexpr, PlanState *parent, MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
 
static void ShutdownSetExpr (Datum arg)
 
static void ExecEvalFuncArgs (FunctionCallInfo fcinfo, List *argList, ExprContext *econtext)
 
static void ExecPrepareTuplestoreResult (SetExprState *sexpr, ExprContext *econtext, Tuplestorestate *resultStore, TupleDesc resultDesc)
 
static void tupledesc_match (TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
 
SetExprStateExecInitTableFunctionResult (Expr *expr, ExprContext *econtext, PlanState *parent)
 
TuplestorestateExecMakeTableFunctionResult (SetExprState *setexpr, ExprContext *econtext, MemoryContext argContext, TupleDesc expectedDesc, bool randomAccess)
 
SetExprStateExecInitFunctionResultSet (Expr *expr, ExprContext *econtext, PlanState *parent)
 
Datum ExecMakeFunctionResultSet (SetExprState *fcache, ExprContext *econtext, MemoryContext argContext, bool *isNull, ExprDoneCond *isDone)
 

Function Documentation

◆ ExecEvalFuncArgs()

static void ExecEvalFuncArgs ( FunctionCallInfo  fcinfo,
List argList,
ExprContext econtext 
)
static

Definition at line 835 of file execSRF.c.

838 {
839  int i;
840  ListCell *arg;
841 
842  i = 0;
843  foreach(arg, argList)
844  {
845  ExprState *argstate = (ExprState *) lfirst(arg);
846 
847  fcinfo->args[i].value = ExecEvalExpr(argstate,
848  econtext,
849  &fcinfo->args[i].isnull);
850  i++;
851  }
852 
853  Assert(i == fcinfo->nargs);
854 }
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:332
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
void * arg
#define lfirst(lc)
Definition: pg_list.h:172
NullableDatum args[FLEXIBLE_ARRAY_MEMBER]
Definition: fmgr.h:95
Datum value
Definition: postgres.h:75
bool isnull
Definition: postgres.h:77

References arg, FunctionCallInfoBaseData::args, Assert(), ExecEvalExpr(), i, NullableDatum::isnull, lfirst, FunctionCallInfoBaseData::nargs, and NullableDatum::value.

Referenced by ExecMakeFunctionResultSet(), and ExecMakeTableFunctionResult().

◆ ExecInitFunctionResultSet()

SetExprState* ExecInitFunctionResultSet ( Expr expr,
ExprContext econtext,
PlanState parent 
)

Definition at line 445 of file execSRF.c.

447 {
449 
450  state->funcReturnsSet = true;
451  state->expr = expr;
452  state->func.fn_oid = InvalidOid;
453 
454  /*
455  * Initialize metadata. The expression node could be either a FuncExpr or
456  * an OpExpr.
457  */
458  if (IsA(expr, FuncExpr))
459  {
460  FuncExpr *func = (FuncExpr *) expr;
461 
462  state->args = ExecInitExprList(func->args, parent);
463  init_sexpr(func->funcid, func->inputcollid, expr, state, parent,
464  econtext->ecxt_per_query_memory, true, true);
465  }
466  else if (IsA(expr, OpExpr))
467  {
468  OpExpr *op = (OpExpr *) expr;
469 
470  state->args = ExecInitExprList(op->args, parent);
471  init_sexpr(op->opfuncid, op->inputcollid, expr, state, parent,
472  econtext->ecxt_per_query_memory, true, true);
473  }
474  else
475  elog(ERROR, "unrecognized node type: %d",
476  (int) nodeTag(expr));
477 
478  /* shouldn't get here unless the selected function returns set */
479  Assert(state->func.fn_retset);
480 
481  return state;
482 }
#define ERROR
Definition: elog.h:39
List * ExecInitExprList(List *nodes, PlanState *parent)
Definition: execExpr.c:323
static void init_sexpr(Oid foid, Oid input_collation, Expr *node, SetExprState *sexpr, PlanState *parent, MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
Definition: execSRF.c:697
#define IsA(nodeptr, _type_)
Definition: nodes.h:179
#define nodeTag(nodeptr)
Definition: nodes.h:133
#define makeNode(_type_)
Definition: nodes.h:176
#define InvalidOid
Definition: postgres_ext.h:36
MemoryContext ecxt_per_query_memory
Definition: execnodes.h:256
Oid funcid
Definition: primnodes.h:677
List * args
Definition: primnodes.h:695
List * args
Definition: primnodes.h:763
Definition: regguts.h:323

References FuncExpr::args, OpExpr::args, Assert(), ExprContext::ecxt_per_query_memory, elog(), ERROR, ExecInitExprList(), FuncExpr::funcid, init_sexpr(), InvalidOid, IsA, makeNode, and nodeTag.

Referenced by ExecInitProjectSet().

◆ ExecInitTableFunctionResult()

SetExprState* ExecInitTableFunctionResult ( Expr expr,
ExprContext econtext,
PlanState parent 
)

Definition at line 57 of file execSRF.c.

59 {
61 
62  state->funcReturnsSet = false;
63  state->expr = expr;
64  state->func.fn_oid = InvalidOid;
65 
66  /*
67  * Normally the passed expression tree will be a FuncExpr, since the
68  * grammar only allows a function call at the top level of a table
69  * function reference. However, if the function doesn't return set then
70  * the planner might have replaced the function call via constant-folding
71  * or inlining. So if we see any other kind of expression node, execute
72  * it via the general ExecEvalExpr() code. That code path will not
73  * support set-returning functions buried in the expression, though.
74  */
75  if (IsA(expr, FuncExpr))
76  {
77  FuncExpr *func = (FuncExpr *) expr;
78 
79  state->funcReturnsSet = func->funcretset;
80  state->args = ExecInitExprList(func->args, parent);
81 
82  init_sexpr(func->funcid, func->inputcollid, expr, state, parent,
83  econtext->ecxt_per_query_memory, func->funcretset, false);
84  }
85  else
86  {
87  state->elidedFuncState = ExecInitExpr(expr, parent);
88  }
89 
90  return state;
91 }
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:128

References FuncExpr::args, ExprContext::ecxt_per_query_memory, ExecInitExpr(), ExecInitExprList(), FuncExpr::funcid, init_sexpr(), InvalidOid, IsA, and makeNode.

Referenced by ExecInitFunctionScan().

◆ ExecMakeFunctionResultSet()

Datum ExecMakeFunctionResultSet ( SetExprState fcache,
ExprContext econtext,
MemoryContext  argContext,
bool isNull,
ExprDoneCond isDone 
)

Definition at line 498 of file execSRF.c.

503 {
504  List *arguments;
505  Datum result;
506  FunctionCallInfo fcinfo;
507  PgStat_FunctionCallUsage fcusage;
508  ReturnSetInfo rsinfo;
509  bool callit;
510  int i;
511 
512 restart:
513 
514  /* Guard against stack overflow due to overly complex expressions */
516 
517  /*
518  * If a previous call of the function returned a set result in the form of
519  * a tuplestore, continue reading rows from the tuplestore until it's
520  * empty.
521  */
522  if (fcache->funcResultStore)
523  {
524  TupleTableSlot *slot = fcache->funcResultSlot;
525  MemoryContext oldContext;
526  bool foundTup;
527 
528  /*
529  * Have to make sure tuple in slot lives long enough, otherwise
530  * clearing the slot could end up trying to free something already
531  * freed.
532  */
533  oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
534  foundTup = tuplestore_gettupleslot(fcache->funcResultStore, true, false,
535  fcache->funcResultSlot);
536  MemoryContextSwitchTo(oldContext);
537 
538  if (foundTup)
539  {
540  *isDone = ExprMultipleResult;
541  if (fcache->funcReturnsTuple)
542  {
543  /* We must return the whole tuple as a Datum. */
544  *isNull = false;
546  }
547  else
548  {
549  /* Extract the first column and return it as a scalar. */
550  return slot_getattr(fcache->funcResultSlot, 1, isNull);
551  }
552  }
553  /* Exhausted the tuplestore, so clean up */
555  fcache->funcResultStore = NULL;
556  *isDone = ExprEndResult;
557  *isNull = true;
558  return (Datum) 0;
559  }
560 
561  /*
562  * arguments is a list of expressions to evaluate before passing to the
563  * function manager. We skip the evaluation if it was already done in the
564  * previous call (ie, we are continuing the evaluation of a set-valued
565  * function). Otherwise, collect the current argument values into fcinfo.
566  *
567  * The arguments have to live in a context that lives at least until all
568  * rows from this SRF have been returned, otherwise ValuePerCall SRFs
569  * would reference freed memory after the first returned row.
570  */
571  fcinfo = fcache->fcinfo;
572  arguments = fcache->args;
573  if (!fcache->setArgsValid)
574  {
575  MemoryContext oldContext = MemoryContextSwitchTo(argContext);
576 
577  ExecEvalFuncArgs(fcinfo, arguments, econtext);
578  MemoryContextSwitchTo(oldContext);
579  }
580  else
581  {
582  /* Reset flag (we may set it again below) */
583  fcache->setArgsValid = false;
584  }
585 
586  /*
587  * Now call the function, passing the evaluated parameter values.
588  */
589 
590  /* Prepare a resultinfo node for communication. */
591  fcinfo->resultinfo = (Node *) &rsinfo;
592  rsinfo.type = T_ReturnSetInfo;
593  rsinfo.econtext = econtext;
594  rsinfo.expectedDesc = fcache->funcResultDesc;
596  /* note we do not set SFRM_Materialize_Random or _Preferred */
597  rsinfo.returnMode = SFRM_ValuePerCall;
598  /* isDone is filled below */
599  rsinfo.setResult = NULL;
600  rsinfo.setDesc = NULL;
601 
602  /*
603  * If function is strict, and there are any NULL arguments, skip calling
604  * the function.
605  */
606  callit = true;
607  if (fcache->func.fn_strict)
608  {
609  for (i = 0; i < fcinfo->nargs; i++)
610  {
611  if (fcinfo->args[i].isnull)
612  {
613  callit = false;
614  break;
615  }
616  }
617  }
618 
619  if (callit)
620  {
621  pgstat_init_function_usage(fcinfo, &fcusage);
622 
623  fcinfo->isnull = false;
624  rsinfo.isDone = ExprSingleResult;
625  result = FunctionCallInvoke(fcinfo);
626  *isNull = fcinfo->isnull;
627  *isDone = rsinfo.isDone;
628 
629  pgstat_end_function_usage(&fcusage,
630  rsinfo.isDone != ExprMultipleResult);
631  }
632  else
633  {
634  /* for a strict SRF, result for NULL is an empty set */
635  result = (Datum) 0;
636  *isNull = true;
637  *isDone = ExprEndResult;
638  }
639 
640  /* Which protocol does function want to use? */
641  if (rsinfo.returnMode == SFRM_ValuePerCall)
642  {
643  if (*isDone != ExprEndResult)
644  {
645  /*
646  * Save the current argument values to re-use on the next call.
647  */
648  if (*isDone == ExprMultipleResult)
649  {
650  fcache->setArgsValid = true;
651  /* Register cleanup callback if we didn't already */
652  if (!fcache->shutdown_reg)
653  {
656  PointerGetDatum(fcache));
657  fcache->shutdown_reg = true;
658  }
659  }
660  }
661  }
662  else if (rsinfo.returnMode == SFRM_Materialize)
663  {
664  /* check we're on the same page as the function author */
665  if (rsinfo.isDone != ExprSingleResult)
666  ereport(ERROR,
667  (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
668  errmsg("table-function protocol for materialize mode was not followed")));
669  if (rsinfo.setResult != NULL)
670  {
671  /* prepare to return values from the tuplestore */
672  ExecPrepareTuplestoreResult(fcache, econtext,
673  rsinfo.setResult,
674  rsinfo.setDesc);
675  /* loop back to top to start returning from tuplestore */
676  goto restart;
677  }
678  /* if setResult was left null, treat it as empty set */
679  *isDone = ExprEndResult;
680  *isNull = true;
681  result = (Datum) 0;
682  }
683  else
684  ereport(ERROR,
685  (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
686  errmsg("unrecognized table-function returnMode: %d",
687  (int) rsinfo.returnMode)));
688 
689  return result;
690 }
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ereport(elevel,...)
Definition: elog.h:149
static void ExecEvalFuncArgs(FunctionCallInfo fcinfo, List *argList, ExprContext *econtext)
Definition: execSRF.c:835
static void ShutdownSetExpr(Datum arg)
Definition: execSRF.c:811
static void ExecPrepareTuplestoreResult(SetExprState *sexpr, ExprContext *econtext, Tuplestorestate *resultStore, TupleDesc resultDesc)
Definition: execSRF.c:865
Datum ExecFetchSlotHeapTupleDatum(TupleTableSlot *slot)
Definition: execTuples.c:1724
void RegisterExprContextCallback(ExprContext *econtext, ExprContextCallbackFunction function, Datum arg)
Definition: execUtils.c:928
@ ExprSingleResult
Definition: execnodes.h:297
@ ExprMultipleResult
Definition: execnodes.h:298
@ ExprEndResult
Definition: execnodes.h:299
@ SFRM_ValuePerCall
Definition: execnodes.h:310
@ SFRM_Materialize
Definition: execnodes.h:311
#define FunctionCallInvoke(fcinfo)
Definition: fmgr.h:172
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
void pgstat_init_function_usage(FunctionCallInfo fcinfo, PgStat_FunctionCallUsage *fcu)
void pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
void check_stack_depth(void)
Definition: postgres.c:3523
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:322
uintptr_t Datum
Definition: postgres.h:64
bool fn_strict
Definition: fmgr.h:61
fmNodePtr resultinfo
Definition: fmgr.h:89
Definition: pg_list.h:54
Definition: nodes.h:129
NodeTag type
Definition: execnodes.h:324
SetFunctionReturnMode returnMode
Definition: execnodes.h:330
ExprContext * econtext
Definition: execnodes.h:326
TupleDesc setDesc
Definition: execnodes.h:334
Tuplestorestate * setResult
Definition: execnodes.h:333
TupleDesc expectedDesc
Definition: execnodes.h:327
int allowedModes
Definition: execnodes.h:328
ExprDoneCond isDone
Definition: execnodes.h:331
FunctionCallInfo fcinfo
Definition: execnodes.h:942
TupleTableSlot * funcResultSlot
Definition: execnodes.h:905
Tuplestorestate * funcResultStore
Definition: execnodes.h:904
bool shutdown_reg
Definition: execnodes.h:935
bool funcReturnsTuple
Definition: execnodes.h:912
TupleDesc funcResultDesc
Definition: execnodes.h:911
FmgrInfo func
Definition: execnodes.h:897
List * args
Definition: execnodes.h:883
bool setArgsValid
Definition: execnodes.h:927
MemoryContext tts_mcxt
Definition: tuptable.h:128
bool tuplestore_gettupleslot(Tuplestorestate *state, bool forward, bool copy, TupleTableSlot *slot)
Definition: tuplestore.c:1078
void tuplestore_end(Tuplestorestate *state)
Definition: tuplestore.c:453
static Datum slot_getattr(TupleTableSlot *slot, int attnum, bool *isnull)
Definition: tuptable.h:388

References ReturnSetInfo::allowedModes, FunctionCallInfoBaseData::args, SetExprState::args, check_stack_depth(), ReturnSetInfo::econtext, ereport, errcode(), errmsg(), ERROR, ExecEvalFuncArgs(), ExecFetchSlotHeapTupleDatum(), ExecPrepareTuplestoreResult(), ReturnSetInfo::expectedDesc, ExprEndResult, ExprMultipleResult, ExprSingleResult, SetExprState::fcinfo, FmgrInfo::fn_strict, SetExprState::func, SetExprState::funcResultDesc, SetExprState::funcResultSlot, SetExprState::funcResultStore, SetExprState::funcReturnsTuple, FunctionCallInvoke, i, ReturnSetInfo::isDone, FunctionCallInfoBaseData::isnull, NullableDatum::isnull, MemoryContextSwitchTo(), FunctionCallInfoBaseData::nargs, pgstat_end_function_usage(), pgstat_init_function_usage(), PointerGetDatum(), RegisterExprContextCallback(), FunctionCallInfoBaseData::resultinfo, ReturnSetInfo::returnMode, SetExprState::setArgsValid, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_ValuePerCall, SetExprState::shutdown_reg, ShutdownSetExpr(), slot_getattr(), TupleTableSlot::tts_mcxt, tuplestore_end(), tuplestore_gettupleslot(), and ReturnSetInfo::type.

Referenced by ExecProjectSRF().

◆ ExecMakeTableFunctionResult()

Tuplestorestate* ExecMakeTableFunctionResult ( SetExprState setexpr,
ExprContext econtext,
MemoryContext  argContext,
TupleDesc  expectedDesc,
bool  randomAccess 
)

Definition at line 102 of file execSRF.c.

107 {
108  Tuplestorestate *tupstore = NULL;
109  TupleDesc tupdesc = NULL;
110  Oid funcrettype;
111  bool returnsTuple;
112  bool returnsSet = false;
113  FunctionCallInfo fcinfo;
114  PgStat_FunctionCallUsage fcusage;
115  ReturnSetInfo rsinfo;
116  HeapTupleData tmptup;
117  MemoryContext callerContext;
118  bool first_time = true;
119 
120  /*
121  * Execute per-tablefunc actions in appropriate context.
122  *
123  * The FunctionCallInfo needs to live across all the calls to a
124  * ValuePerCall function, so it can't be allocated in the per-tuple
125  * context. Similarly, the function arguments need to be evaluated in a
126  * context that is longer lived than the per-tuple context: The argument
127  * values would otherwise disappear when we reset that context in the
128  * inner loop. As the caller's CurrentMemoryContext is typically a
129  * query-lifespan context, we don't want to leak memory there. We require
130  * the caller to pass a separate memory context that can be used for this,
131  * and can be reset each time through to avoid bloat.
132  */
133  MemoryContextReset(argContext);
134  callerContext = MemoryContextSwitchTo(argContext);
135 
136  funcrettype = exprType((Node *) setexpr->expr);
137 
138  returnsTuple = type_is_rowtype(funcrettype);
139 
140  /*
141  * Prepare a resultinfo node for communication. We always do this even if
142  * not expecting a set result, so that we can pass expectedDesc. In the
143  * generic-expression case, the expression doesn't actually get to see the
144  * resultinfo, but set it up anyway because we use some of the fields as
145  * our own state variables.
146  */
147  rsinfo.type = T_ReturnSetInfo;
148  rsinfo.econtext = econtext;
149  rsinfo.expectedDesc = expectedDesc;
151  if (randomAccess)
152  rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
153  rsinfo.returnMode = SFRM_ValuePerCall;
154  /* isDone is filled below */
155  rsinfo.setResult = NULL;
156  rsinfo.setDesc = NULL;
157 
158  fcinfo = palloc(SizeForFunctionCallInfo(list_length(setexpr->args)));
159 
160  /*
161  * Normally the passed expression tree will be a SetExprState, since the
162  * grammar only allows a function call at the top level of a table
163  * function reference. However, if the function doesn't return set then
164  * the planner might have replaced the function call via constant-folding
165  * or inlining. So if we see any other kind of expression node, execute
166  * it via the general ExecEvalExpr() code; the only difference is that we
167  * don't get a chance to pass a special ReturnSetInfo to any functions
168  * buried in the expression.
169  */
170  if (!setexpr->elidedFuncState)
171  {
172  /*
173  * This path is similar to ExecMakeFunctionResultSet.
174  */
175  returnsSet = setexpr->funcReturnsSet;
176  InitFunctionCallInfoData(*fcinfo, &(setexpr->func),
177  list_length(setexpr->args),
178  setexpr->fcinfo->fncollation,
179  NULL, (Node *) &rsinfo);
180  /* evaluate the function's argument list */
181  Assert(CurrentMemoryContext == argContext);
182  ExecEvalFuncArgs(fcinfo, setexpr->args, econtext);
183 
184  /*
185  * If function is strict, and there are any NULL arguments, skip
186  * calling the function and act like it returned NULL (or an empty
187  * set, in the returns-set case).
188  */
189  if (setexpr->func.fn_strict)
190  {
191  int i;
192 
193  for (i = 0; i < fcinfo->nargs; i++)
194  {
195  if (fcinfo->args[i].isnull)
196  goto no_function_result;
197  }
198  }
199  }
200  else
201  {
202  /* Treat setexpr as a generic expression */
203  InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
204  }
205 
206  /*
207  * Switch to short-lived context for calling the function or expression.
208  */
210 
211  /*
212  * Loop to handle the ValuePerCall protocol (which is also the same
213  * behavior needed in the generic ExecEvalExpr path).
214  */
215  for (;;)
216  {
217  Datum result;
218 
220 
221  /*
222  * Reset per-tuple memory context before each call of the function or
223  * expression. This cleans up any local memory the function may leak
224  * when called.
225  */
226  ResetExprContext(econtext);
227 
228  /* Call the function or expression one time */
229  if (!setexpr->elidedFuncState)
230  {
231  pgstat_init_function_usage(fcinfo, &fcusage);
232 
233  fcinfo->isnull = false;
234  rsinfo.isDone = ExprSingleResult;
235  result = FunctionCallInvoke(fcinfo);
236 
237  pgstat_end_function_usage(&fcusage,
238  rsinfo.isDone != ExprMultipleResult);
239  }
240  else
241  {
242  result =
243  ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo->isnull);
244  rsinfo.isDone = ExprSingleResult;
245  }
246 
247  /* Which protocol does function want to use? */
248  if (rsinfo.returnMode == SFRM_ValuePerCall)
249  {
250  /*
251  * Check for end of result set.
252  */
253  if (rsinfo.isDone == ExprEndResult)
254  break;
255 
256  /*
257  * If first time through, build tuplestore for result. For a
258  * scalar function result type, also make a suitable tupdesc.
259  */
260  if (first_time)
261  {
262  MemoryContext oldcontext =
264 
265  tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
266  rsinfo.setResult = tupstore;
267  if (!returnsTuple)
268  {
269  tupdesc = CreateTemplateTupleDesc(1);
270  TupleDescInitEntry(tupdesc,
271  (AttrNumber) 1,
272  "column",
273  funcrettype,
274  -1,
275  0);
276  rsinfo.setDesc = tupdesc;
277  }
278  MemoryContextSwitchTo(oldcontext);
279  }
280 
281  /*
282  * Store current resultset item.
283  */
284  if (returnsTuple)
285  {
286  if (!fcinfo->isnull)
287  {
289 
290  if (tupdesc == NULL)
291  {
292  MemoryContext oldcontext =
294 
295  /*
296  * This is the first non-NULL result from the
297  * function. Use the type info embedded in the
298  * rowtype Datum to look up the needed tupdesc. Make
299  * a copy for the query.
300  */
303  rsinfo.setDesc = tupdesc;
304  MemoryContextSwitchTo(oldcontext);
305  }
306  else
307  {
308  /*
309  * Verify all later returned rows have same subtype;
310  * necessary in case the type is RECORD.
311  */
312  if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
313  HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod)
314  ereport(ERROR,
315  (errcode(ERRCODE_DATATYPE_MISMATCH),
316  errmsg("rows returned by function are not all of the same row type")));
317  }
318 
319  /*
320  * tuplestore_puttuple needs a HeapTuple not a bare
321  * HeapTupleHeader, but it doesn't need all the fields.
322  */
324  tmptup.t_data = td;
325 
326  tuplestore_puttuple(tupstore, &tmptup);
327  }
328  else
329  {
330  /*
331  * NULL result from a tuple-returning function; expand it
332  * to a row of all nulls. We rely on the expectedDesc to
333  * form such rows. (Note: this would be problematic if
334  * tuplestore_putvalues saved the tdtypeid/tdtypmod from
335  * the provided descriptor, since that might not match
336  * what we get from the function itself. But it doesn't.)
337  */
338  int natts = expectedDesc->natts;
339  bool *nullflags;
340 
341  nullflags = (bool *) palloc(natts * sizeof(bool));
342  memset(nullflags, true, natts * sizeof(bool));
343  tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
344  }
345  }
346  else
347  {
348  /* Scalar-type case: just store the function result */
349  tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo->isnull);
350  }
351 
352  /*
353  * Are we done?
354  */
355  if (rsinfo.isDone != ExprMultipleResult)
356  break;
357 
358  /*
359  * Check that set-returning functions were properly declared.
360  * (Note: for historical reasons, we don't complain if a non-SRF
361  * returns ExprEndResult; that's treated as returning NULL.)
362  */
363  if (!returnsSet)
364  ereport(ERROR,
365  (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
366  errmsg("table-function protocol for value-per-call mode was not followed")));
367  }
368  else if (rsinfo.returnMode == SFRM_Materialize)
369  {
370  /* check we're on the same page as the function author */
371  if (!first_time || rsinfo.isDone != ExprSingleResult || !returnsSet)
372  ereport(ERROR,
373  (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
374  errmsg("table-function protocol for materialize mode was not followed")));
375  /* Done evaluating the set result */
376  break;
377  }
378  else
379  ereport(ERROR,
380  (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
381  errmsg("unrecognized table-function returnMode: %d",
382  (int) rsinfo.returnMode)));
383 
384  first_time = false;
385  }
386 
387 no_function_result:
388 
389  /*
390  * If we got nothing from the function (ie, an empty-set or NULL result),
391  * we have to create the tuplestore to return, and if it's a
392  * non-set-returning function then insert a single all-nulls row. As
393  * above, we depend on the expectedDesc to manufacture the dummy row.
394  */
395  if (rsinfo.setResult == NULL)
396  {
397  MemoryContext oldcontext =
399 
400  tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
401  rsinfo.setResult = tupstore;
402  MemoryContextSwitchTo(oldcontext);
403 
404  if (!returnsSet)
405  {
406  int natts = expectedDesc->natts;
407  bool *nullflags;
408 
409  nullflags = (bool *) palloc(natts * sizeof(bool));
410  memset(nullflags, true, natts * sizeof(bool));
411  tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
412  }
413  }
414 
415  /*
416  * If function provided a tupdesc, cross-check it. We only really need to
417  * do this for functions returning RECORD, but might as well do it always.
418  */
419  if (rsinfo.setDesc)
420  {
421  tupledesc_match(expectedDesc, rsinfo.setDesc);
422 
423  /*
424  * If it is a dynamically-allocated TupleDesc, free it: it is
425  * typically allocated in a per-query context, so we must avoid
426  * leaking it across multiple usages.
427  */
428  if (rsinfo.setDesc->tdrefcount == -1)
429  FreeTupleDesc(rsinfo.setDesc);
430  }
431 
432  MemoryContextSwitchTo(callerContext);
433 
434  /* All done, pass back the tuplestore */
435  return rsinfo.setResult;
436 }
int16 AttrNumber
Definition: attnum.h:21
static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
Definition: execSRF.c:944
@ SFRM_Materialize_Preferred
Definition: execnodes.h:313
@ SFRM_Materialize_Random
Definition: execnodes.h:312
#define ResetExprContext(econtext)
Definition: executor.h:543
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
#define SizeForFunctionCallInfo(nargs)
Definition: fmgr.h:102
#define InitFunctionCallInfoData(Fcinfo, Flinfo, Nargs, Collation, Context, Resultinfo)
Definition: fmgr.h:150
int work_mem
Definition: globals.c:125
#define HeapTupleHeaderGetTypMod(tup)
Definition: htup_details.h:466
#define HeapTupleHeaderGetTypeId(tup)
Definition: htup_details.h:456
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:450
bool type_is_rowtype(Oid typid)
Definition: lsyscache.c:2637
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:330
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void * palloc(Size size)
Definition: mcxt.c:1226
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:43
static int list_length(const List *l)
Definition: pg_list.h:152
unsigned int Oid
Definition: postgres_ext.h:31
MemoryContext ecxt_per_tuple_memory
Definition: execnodes.h:257
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68
Expr * expr
Definition: execnodes.h:882
bool funcReturnsSet
Definition: execnodes.h:918
ExprState * elidedFuncState
Definition: execnodes.h:890
int tdrefcount
Definition: tupdesc.h:84
int32 tdtypmod
Definition: tupdesc.h:83
Oid tdtypeid
Definition: tupdesc.h:82
void FreeTupleDesc(TupleDesc tupdesc)
Definition: tupdesc.c:309
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:45
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:583
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, Datum *values, bool *isnull)
Definition: tuplestore.c:750
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition: tuplestore.c:318
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition: tuplestore.c:730
TupleDesc lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
Definition: typcache.c:1864

References ReturnSetInfo::allowedModes, FunctionCallInfoBaseData::args, SetExprState::args, Assert(), CHECK_FOR_INTERRUPTS, CreateTemplateTupleDesc(), CurrentMemoryContext, DatumGetHeapTupleHeader, ReturnSetInfo::econtext, ExprContext::ecxt_per_query_memory, ExprContext::ecxt_per_tuple_memory, SetExprState::elidedFuncState, ereport, errcode(), errmsg(), ERROR, ExecEvalExpr(), ExecEvalFuncArgs(), ReturnSetInfo::expectedDesc, SetExprState::expr, ExprEndResult, ExprMultipleResult, ExprSingleResult, exprType(), SetExprState::fcinfo, FmgrInfo::fn_strict, FunctionCallInfoBaseData::fncollation, FreeTupleDesc(), SetExprState::func, SetExprState::funcReturnsSet, FunctionCallInvoke, HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, InitFunctionCallInfoData, InvalidOid, ReturnSetInfo::isDone, FunctionCallInfoBaseData::isnull, NullableDatum::isnull, list_length(), lookup_rowtype_tupdesc_copy(), MemoryContextReset(), MemoryContextSwitchTo(), FunctionCallInfoBaseData::nargs, TupleDescData::natts, palloc(), pgstat_end_function_usage(), pgstat_init_function_usage(), ResetExprContext, ReturnSetInfo::returnMode, ReturnSetInfo::setDesc, ReturnSetInfo::setResult, SFRM_Materialize, SFRM_Materialize_Preferred, SFRM_Materialize_Random, SFRM_ValuePerCall, SizeForFunctionCallInfo, HeapTupleData::t_data, HeapTupleData::t_len, TupleDescData::tdrefcount, TupleDescData::tdtypeid, TupleDescData::tdtypmod, tupledesc_match(), TupleDescInitEntry(), tuplestore_begin_heap(), tuplestore_puttuple(), tuplestore_putvalues(), ReturnSetInfo::type, type_is_rowtype(), and work_mem.

Referenced by FunctionNext().

◆ ExecPrepareTuplestoreResult()

static void ExecPrepareTuplestoreResult ( SetExprState sexpr,
ExprContext econtext,
Tuplestorestate resultStore,
TupleDesc  resultDesc 
)
static

Definition at line 865 of file execSRF.c.

869 {
870  sexpr->funcResultStore = resultStore;
871 
872  if (sexpr->funcResultSlot == NULL)
873  {
874  /* Create a slot so we can read data out of the tuplestore */
875  TupleDesc slotDesc;
876  MemoryContext oldcontext;
877 
878  oldcontext = MemoryContextSwitchTo(sexpr->func.fn_mcxt);
879 
880  /*
881  * If we were not able to determine the result rowtype from context,
882  * and the function didn't return a tupdesc, we have to fail.
883  */
884  if (sexpr->funcResultDesc)
885  slotDesc = sexpr->funcResultDesc;
886  else if (resultDesc)
887  {
888  /* don't assume resultDesc is long-lived */
889  slotDesc = CreateTupleDescCopy(resultDesc);
890  }
891  else
892  {
893  ereport(ERROR,
894  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
895  errmsg("function returning setof record called in "
896  "context that cannot accept type record")));
897  slotDesc = NULL; /* keep compiler quiet */
898  }
899 
900  sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc,
902  MemoryContextSwitchTo(oldcontext);
903  }
904 
905  /*
906  * If function provided a tupdesc, cross-check it. We only really need to
907  * do this for functions returning RECORD, but might as well do it always.
908  */
909  if (resultDesc)
910  {
911  if (sexpr->funcResultDesc)
912  tupledesc_match(sexpr->funcResultDesc, resultDesc);
913 
914  /*
915  * If it is a dynamically-allocated TupleDesc, free it: it is
916  * typically allocated in a per-query context, so we must avoid
917  * leaking it across multiple usages.
918  */
919  if (resultDesc->tdrefcount == -1)
920  FreeTupleDesc(resultDesc);
921  }
922 
923  /* Register cleanup callback if we didn't already */
924  if (!sexpr->shutdown_reg)
925  {
928  PointerGetDatum(sexpr));
929  sexpr->shutdown_reg = true;
930  }
931 }
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1239
MemoryContext fn_mcxt
Definition: fmgr.h:65
TupleDesc CreateTupleDescCopy(TupleDesc tupdesc)
Definition: tupdesc.c:111

References CreateTupleDescCopy(), ereport, errcode(), errmsg(), ERROR, FmgrInfo::fn_mcxt, FreeTupleDesc(), SetExprState::func, SetExprState::funcResultDesc, SetExprState::funcResultSlot, SetExprState::funcResultStore, MakeSingleTupleTableSlot(), MemoryContextSwitchTo(), PointerGetDatum(), RegisterExprContextCallback(), SetExprState::shutdown_reg, ShutdownSetExpr(), TupleDescData::tdrefcount, TTSOpsMinimalTuple, and tupledesc_match().

Referenced by ExecMakeFunctionResultSet().

◆ init_sexpr()

static void init_sexpr ( Oid  foid,
Oid  input_collation,
Expr node,
SetExprState sexpr,
PlanState parent,
MemoryContext  sexprCxt,
bool  allowSRF,
bool  needDescForSRF 
)
static

Definition at line 697 of file execSRF.c.

700 {
701  AclResult aclresult;
702  size_t numargs = list_length(sexpr->args);
703 
704  /* Check permission to call function */
705  aclresult = object_aclcheck(ProcedureRelationId, foid, GetUserId(), ACL_EXECUTE);
706  if (aclresult != ACLCHECK_OK)
707  aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(foid));
709 
710  /*
711  * Safety check on nargs. Under normal circumstances this should never
712  * fail, as parser should check sooner. But possibly it might fail if
713  * server has been compiled with FUNC_MAX_ARGS smaller than some functions
714  * declared in pg_proc?
715  */
716  if (list_length(sexpr->args) > FUNC_MAX_ARGS)
717  ereport(ERROR,
718  (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
719  errmsg_plural("cannot pass more than %d argument to a function",
720  "cannot pass more than %d arguments to a function",
722  FUNC_MAX_ARGS)));
723 
724  /* Set up the primary fmgr lookup information */
725  fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
726  fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
727 
728  /* Initialize the function call parameter struct as well */
729  sexpr->fcinfo =
731  InitFunctionCallInfoData(*sexpr->fcinfo, &(sexpr->func),
732  numargs,
733  input_collation, NULL, NULL);
734 
735  /* If function returns set, check if that's allowed by caller */
736  if (sexpr->func.fn_retset && !allowSRF)
737  ereport(ERROR,
738  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
739  errmsg("set-valued function called in context that cannot accept a set"),
740  parent ? executor_errposition(parent->state,
741  exprLocation((Node *) node)) : 0));
742 
743  /* Otherwise, caller should have marked the sexpr correctly */
744  Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet);
745 
746  /* If function returns set, prepare expected tuple descriptor */
747  if (sexpr->func.fn_retset && needDescForSRF)
748  {
749  TypeFuncClass functypclass;
750  Oid funcrettype;
751  TupleDesc tupdesc;
752  MemoryContext oldcontext;
753 
754  functypclass = get_expr_result_type(sexpr->func.fn_expr,
755  &funcrettype,
756  &tupdesc);
757 
758  /* Must save tupdesc in sexpr's context */
759  oldcontext = MemoryContextSwitchTo(sexprCxt);
760 
761  if (functypclass == TYPEFUNC_COMPOSITE ||
762  functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
763  {
764  /* Composite data type, e.g. a table's row type */
765  Assert(tupdesc);
766  /* Must copy it out of typcache for safety */
767  sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc);
768  sexpr->funcReturnsTuple = true;
769  }
770  else if (functypclass == TYPEFUNC_SCALAR)
771  {
772  /* Base data type, i.e. scalar */
773  tupdesc = CreateTemplateTupleDesc(1);
774  TupleDescInitEntry(tupdesc,
775  (AttrNumber) 1,
776  NULL,
777  funcrettype,
778  -1,
779  0);
780  sexpr->funcResultDesc = tupdesc;
781  sexpr->funcReturnsTuple = false;
782  }
783  else if (functypclass == TYPEFUNC_RECORD)
784  {
785  /* This will work if function doesn't need an expectedDesc */
786  sexpr->funcResultDesc = NULL;
787  sexpr->funcReturnsTuple = true;
788  }
789  else
790  {
791  /* Else, we will fail if function needs an expectedDesc */
792  sexpr->funcResultDesc = NULL;
793  }
794 
795  MemoryContextSwitchTo(oldcontext);
796  }
797  else
798  sexpr->funcResultDesc = NULL;
799 
800  /* Initialize additional state */
801  sexpr->funcResultStore = NULL;
802  sexpr->funcResultSlot = NULL;
803  sexpr->shutdown_reg = false;
804 }
AclResult
Definition: acl.h:181
@ ACLCHECK_OK
Definition: acl.h:182
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2669
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3760
int errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition: elog.c:1179
int executor_errposition(EState *estate, int location)
Definition: execUtils.c:901
void fmgr_info_cxt(Oid functionId, FmgrInfo *finfo, MemoryContext mcxt)
Definition: fmgr.c:137
struct FunctionCallInfoBaseData * FunctionCallInfo
Definition: fmgr.h:38
#define fmgr_info_set_expr(expr, finfo)
Definition: fmgr.h:135
TypeFuncClass get_expr_result_type(Node *expr, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:292
TypeFuncClass
Definition: funcapi.h:147
@ TYPEFUNC_SCALAR
Definition: funcapi.h:148
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
@ TYPEFUNC_RECORD
Definition: funcapi.h:151
@ TYPEFUNC_COMPOSITE_DOMAIN
Definition: funcapi.h:150
char * get_func_name(Oid funcid)
Definition: lsyscache.c:1612
Oid GetUserId(void)
Definition: miscinit.c:509
int exprLocation(const Node *expr)
Definition: nodeFuncs.c:1312
#define InvokeFunctionExecuteHook(objectId)
Definition: objectaccess.h:213
@ OBJECT_FUNCTION
Definition: parsenodes.h:2139
#define ACL_EXECUTE
Definition: parsenodes.h:90
#define FUNC_MAX_ARGS
fmNodePtr fn_expr
Definition: fmgr.h:66
bool fn_retset
Definition: fmgr.h:62
EState * state
Definition: execnodes.h:1039

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, SetExprState::args, Assert(), CreateTemplateTupleDesc(), CreateTupleDescCopy(), ereport, errcode(), errmsg(), errmsg_plural(), ERROR, executor_errposition(), SetExprState::expr, exprLocation(), SetExprState::fcinfo, fmgr_info_cxt(), fmgr_info_set_expr, FmgrInfo::fn_expr, FmgrInfo::fn_retset, SetExprState::func, FUNC_MAX_ARGS, SetExprState::funcResultDesc, SetExprState::funcResultSlot, SetExprState::funcResultStore, SetExprState::funcReturnsSet, SetExprState::funcReturnsTuple, get_expr_result_type(), get_func_name(), GetUserId(), InitFunctionCallInfoData, InvokeFunctionExecuteHook, list_length(), MemoryContextSwitchTo(), object_aclcheck(), OBJECT_FUNCTION, palloc(), SetExprState::shutdown_reg, SizeForFunctionCallInfo, PlanState::state, TupleDescInitEntry(), TYPEFUNC_COMPOSITE, TYPEFUNC_COMPOSITE_DOMAIN, TYPEFUNC_RECORD, and TYPEFUNC_SCALAR.

Referenced by ExecInitFunctionResultSet(), and ExecInitTableFunctionResult().

◆ ShutdownSetExpr()

static void ShutdownSetExpr ( Datum  arg)
static

Definition at line 811 of file execSRF.c.

812 {
814 
815  /* If we have a slot, make sure it's let go of any tuplestore pointer */
816  if (sexpr->funcResultSlot)
818 
819  /* Release any open tuplestore */
820  if (sexpr->funcResultStore)
822  sexpr->funcResultStore = NULL;
823 
824  /* Clear any active set-argument state */
825  sexpr->setArgsValid = false;
826 
827  /* execUtils will deregister the callback... */
828  sexpr->shutdown_reg = false;
829 }
#define castNode(_type_, nodeptr)
Definition: nodes.h:197
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:312
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:432

References arg, castNode, DatumGetPointer(), ExecClearTuple(), SetExprState::funcResultSlot, SetExprState::funcResultStore, SetExprState::setArgsValid, SetExprState::shutdown_reg, and tuplestore_end().

Referenced by ExecMakeFunctionResultSet(), and ExecPrepareTuplestoreResult().

◆ tupledesc_match()

static void tupledesc_match ( TupleDesc  dst_tupdesc,
TupleDesc  src_tupdesc 
)
static

Definition at line 944 of file execSRF.c.

945 {
946  int i;
947 
948  if (dst_tupdesc->natts != src_tupdesc->natts)
949  ereport(ERROR,
950  (errcode(ERRCODE_DATATYPE_MISMATCH),
951  errmsg("function return row and query-specified return row do not match"),
952  errdetail_plural("Returned row contains %d attribute, but query expects %d.",
953  "Returned row contains %d attributes, but query expects %d.",
954  src_tupdesc->natts,
955  src_tupdesc->natts, dst_tupdesc->natts)));
956 
957  for (i = 0; i < dst_tupdesc->natts; i++)
958  {
959  Form_pg_attribute dattr = TupleDescAttr(dst_tupdesc, i);
960  Form_pg_attribute sattr = TupleDescAttr(src_tupdesc, i);
961 
962  if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
963  continue; /* no worries */
964  if (!dattr->attisdropped)
965  ereport(ERROR,
966  (errcode(ERRCODE_DATATYPE_MISMATCH),
967  errmsg("function return row and query-specified return row do not match"),
968  errdetail("Returned type %s at ordinal position %d, but query expects %s.",
969  format_type_be(sattr->atttypid),
970  i + 1,
971  format_type_be(dattr->atttypid))));
972 
973  if (dattr->attlen != sattr->attlen ||
974  dattr->attalign != sattr->attalign)
975  ereport(ERROR,
976  (errcode(ERRCODE_DATATYPE_MISMATCH),
977  errmsg("function return row and query-specified return row do not match"),
978  errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
979  i + 1)));
980  }
981 }
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errdetail_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...)
Definition: elog.c:1294
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
bool IsBinaryCoercible(Oid srctype, Oid targettype)
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:92

References ereport, errcode(), errdetail(), errdetail_plural(), errmsg(), ERROR, format_type_be(), i, IsBinaryCoercible(), TupleDescData::natts, and TupleDescAttr.

Referenced by ExecMakeTableFunctionResult(), and ExecPrepareTuplestoreResult().