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 "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 834 of file execSRF.c.

837 {
838  int i;
839  ListCell *arg;
840 
841  i = 0;
842  foreach(arg, argList)
843  {
844  ExprState *argstate = (ExprState *) lfirst(arg);
845 
846  fcinfo->args[i].value = ExecEvalExpr(argstate,
847  econtext,
848  &fcinfo->args[i].isnull);
849  i++;
850  }
851 
852  Assert(i == fcinfo->nargs);
853 }
#define Assert(condition)
Definition: c.h:858
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:333
int i
Definition: isn.c:73
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 444 of file execSRF.c.

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

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

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 497 of file execSRF.c.

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

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 101 of file execSRF.c.

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

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 864 of file execSRF.c.

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

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 696 of file execSRF.c.

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

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 810 of file execSRF.c.

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

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 943 of file execSRF.c.

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