PostgreSQL Source Code git master
Loading...
Searching...
No Matches
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/tuplestore.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 837 of file execSRF.c.

840{
841 int i;
842 ListCell *arg;
843
844 i = 0;
845 foreach(arg, argList)
846 {
848
849 fcinfo->args[i].value = ExecEvalExpr(argstate,
850 econtext,
851 &fcinfo->args[i].isnull);
852 i++;
853 }
854
855 Assert(i == fcinfo->nargs);
856}
#define Assert(condition)
Definition c.h:945
Datum arg
Definition elog.c:1322
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:396
int i
Definition isn.c:77
#define lfirst(lc)
Definition pg_list.h:172
static int fb(int x)
NullableDatum args[FLEXIBLE_ARRAY_MEMBER]
Definition fmgr.h:95
Datum value
Definition postgres.h:87

References arg, FunctionCallInfoBaseData::args, Assert, ExecEvalExpr(), fb(), 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 446 of file execSRF.c.

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

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:143

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

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

References FunctionCallInfoBaseData::args, check_stack_depth(), ereport, errcode(), errmsg, ERROR, ExecEvalFuncArgs(), ExecFetchSlotHeapTupleDatum(), ExecPrepareTuplestoreResult(), ExprEndResult, ExprMultipleResult, ExprSingleResult, fb(), FunctionCallInvoke, i, FunctionCallInfoBaseData::isnull, NullableDatum::isnull, MemoryContextSwitchTo(), FunctionCallInfoBaseData::nargs, pgstat_end_function_usage(), pgstat_init_function_usage(), PointerGetDatum(), RegisterExprContextCallback(), FunctionCallInfoBaseData::resultinfo, SFRM_Materialize, SFRM_ValuePerCall, ShutdownSetExpr(), slot_getattr(), TupleTableSlot::tts_mcxt, tuplestore_end(), tuplestore_gettupleslot(), and Node::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;
111 bool returnsTuple;
112 bool returnsSet = false;
113 FunctionCallInfo fcinfo;
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 */
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 */
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)
197 }
198 }
199 }
200 else
201 {
202 /* Treat setexpr as a generic expression */
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 {
232
233 fcinfo->isnull = false;
234 rsinfo.isDone = ExprSingleResult;
235 result = FunctionCallInvoke(fcinfo);
236
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",
274 -1,
275 0);
276 TupleDescFinalize(tupdesc);
277 rsinfo.setDesc = tupdesc;
278 }
279 MemoryContextSwitchTo(oldcontext);
280 }
281
282 /*
283 * Store current resultset item.
284 */
285 if (returnsTuple)
286 {
287 if (!fcinfo->isnull)
288 {
290
291 if (tupdesc == NULL)
292 {
293 MemoryContext oldcontext =
295
296 /*
297 * This is the first non-NULL result from the
298 * function. Use the type info embedded in the
299 * rowtype Datum to look up the needed tupdesc. Make
300 * a copy for the query.
301 */
304 rsinfo.setDesc = tupdesc;
305 MemoryContextSwitchTo(oldcontext);
306 }
307 else
308 {
309 /*
310 * Verify all later returned rows have same subtype;
311 * necessary in case the type is RECORD.
312 */
313 if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
314 HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod)
317 errmsg("rows returned by function are not all of the same row type")));
318 }
319
320 /*
321 * tuplestore_puttuple needs a HeapTuple not a bare
322 * HeapTupleHeader, but it doesn't need all the fields.
323 */
325 tmptup.t_data = td;
326
327 tuplestore_puttuple(tupstore, &tmptup);
328 }
329 else
330 {
331 /*
332 * NULL result from a tuple-returning function; expand it
333 * to a row of all nulls. We rely on the expectedDesc to
334 * form such rows. (Note: this would be problematic if
335 * tuplestore_putvalues saved the tdtypeid/tdtypmod from
336 * the provided descriptor, since that might not match
337 * what we get from the function itself. But it doesn't.)
338 */
339 int natts = expectedDesc->natts;
340 bool *nullflags;
341
342 nullflags = (bool *) palloc(natts * sizeof(bool));
343 memset(nullflags, true, natts * sizeof(bool));
344 tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
345 }
346 }
347 else
348 {
349 /* Scalar-type case: just store the function result */
350 tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo->isnull);
351 }
352
353 /*
354 * Are we done?
355 */
356 if (rsinfo.isDone != ExprMultipleResult)
357 break;
358
359 /*
360 * Check that set-returning functions were properly declared.
361 * (Note: for historical reasons, we don't complain if a non-SRF
362 * returns ExprEndResult; that's treated as returning NULL.)
363 */
364 if (!returnsSet)
367 errmsg("table-function protocol for value-per-call mode was not followed")));
368 }
369 else if (rsinfo.returnMode == SFRM_Materialize)
370 {
371 /* check we're on the same page as the function author */
372 if (!first_time || rsinfo.isDone != ExprSingleResult || !returnsSet)
375 errmsg("table-function protocol for materialize mode was not followed")));
376 /* Done evaluating the set result */
377 break;
378 }
379 else
382 errmsg("unrecognized table-function returnMode: %d",
383 (int) rsinfo.returnMode)));
384
385 first_time = false;
386 }
387
389
390 /*
391 * If we got nothing from the function (ie, an empty-set or NULL result),
392 * we have to create the tuplestore to return, and if it's a
393 * non-set-returning function then insert a single all-nulls row. As
394 * above, we depend on the expectedDesc to manufacture the dummy row.
395 */
396 if (rsinfo.setResult == NULL)
397 {
398 MemoryContext oldcontext =
400
401 tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
402 rsinfo.setResult = tupstore;
403 MemoryContextSwitchTo(oldcontext);
404
405 if (!returnsSet)
406 {
407 int natts = expectedDesc->natts;
408 bool *nullflags;
409
410 nullflags = (bool *) palloc(natts * sizeof(bool));
411 memset(nullflags, true, natts * sizeof(bool));
412 tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
413 }
414 }
415
416 /*
417 * If function provided a tupdesc, cross-check it. We only really need to
418 * do this for functions returning RECORD, but might as well do it always.
419 */
420 if (rsinfo.setDesc)
421 {
422 tupledesc_match(expectedDesc, rsinfo.setDesc);
423
424 /*
425 * If it is a dynamically-allocated TupleDesc, free it: it is
426 * typically allocated in a per-query context, so we must avoid
427 * leaking it across multiple usages.
428 */
429 if (rsinfo.setDesc->tdrefcount == -1)
430 FreeTupleDesc(rsinfo.setDesc);
431 }
432
434
435 /* All done, pass back the tuplestore */
436 return rsinfo.setResult;
437}
int16 AttrNumber
Definition attnum.h:21
static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
Definition execSRF.c:946
@ SFRM_Materialize_Preferred
Definition execnodes.h:354
@ SFRM_Materialize_Random
Definition execnodes.h:353
#define ResetExprContext(econtext)
Definition executor.h:654
#define DatumGetHeapTupleHeader(X)
Definition fmgr.h:296
#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:131
static int32 HeapTupleHeaderGetTypMod(const HeapTupleHeaderData *tup)
static uint32 HeapTupleHeaderGetDatumLength(const HeapTupleHeaderData *tup)
static Oid HeapTupleHeaderGetTypeId(const HeapTupleHeaderData *tup)
bool type_is_rowtype(Oid typid)
Definition lsyscache.c:2877
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:403
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
static int list_length(const List *l)
Definition pg_list.h:152
unsigned int Oid
MemoryContext ecxt_per_tuple_memory
Definition execnodes.h:292
bool fn_strict
Definition fmgr.h:61
Expr * expr
Definition execnodes.h:954
FunctionCallInfo fcinfo
Definition execnodes.h:1014
bool funcReturnsSet
Definition execnodes.h:990
ExprState * elidedFuncState
Definition execnodes.h:962
FmgrInfo func
Definition execnodes.h:969
List * args
Definition execnodes.h:955
int32 tdtypmod
Definition tupdesc.h:152
void FreeTupleDesc(TupleDesc tupdesc)
Definition tupdesc.c:557
TupleDesc CreateTemplateTupleDesc(int natts)
Definition tupdesc.c:165
void TupleDescFinalize(TupleDesc tupdesc)
Definition tupdesc.c:508
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:897
Tuplestorestate * tuplestore_begin_heap(bool randomAccess, bool interXact, int maxKBytes)
Definition tuplestore.c:331
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition tuplestore.c:785
void tuplestore_puttuple(Tuplestorestate *state, HeapTuple tuple)
Definition tuplestore.c:765
TupleDesc lookup_rowtype_tupdesc_copy(Oid type_id, int32 typmod)
Definition typcache.c:1981

References FunctionCallInfoBaseData::args, SetExprState::args, Assert, CHECK_FOR_INTERRUPTS, CreateTemplateTupleDesc(), CurrentMemoryContext, DatumGetHeapTupleHeader, ExprContext::ecxt_per_query_memory, ExprContext::ecxt_per_tuple_memory, SetExprState::elidedFuncState, ereport, errcode(), errmsg, ERROR, ExecEvalExpr(), ExecEvalFuncArgs(), SetExprState::expr, ExprEndResult, ExprMultipleResult, ExprSingleResult, exprType(), fb(), SetExprState::fcinfo, FmgrInfo::fn_strict, FunctionCallInfoBaseData::fncollation, FreeTupleDesc(), SetExprState::func, SetExprState::funcReturnsSet, FunctionCallInvoke, HeapTupleHeaderGetDatumLength(), HeapTupleHeaderGetTypeId(), HeapTupleHeaderGetTypMod(), i, InitFunctionCallInfoData, InvalidOid, 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, SFRM_Materialize, SFRM_Materialize_Preferred, SFRM_Materialize_Random, SFRM_ValuePerCall, SizeForFunctionCallInfo, TupleDescData::tdtypeid, TupleDescData::tdtypmod, tupledesc_match(), TupleDescFinalize(), TupleDescInitEntry(), tuplestore_begin_heap(), tuplestore_puttuple(), tuplestore_putvalues(), 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 867 of file execSRF.c.

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

References CreateTupleDescCopy(), ereport, errcode(), errmsg, ERROR, fb(), FreeTupleDesc(), MakeSingleTupleTableSlot(), MemoryContextSwitchTo(), PointerGetDatum(), RegisterExprContextCallback(), 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 698 of file execSRF.c.

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

References ACL_EXECUTE, aclcheck_error(), ACLCHECK_OK, Assert, CreateTemplateTupleDesc(), CreateTupleDescCopy(), ereport, errcode(), errmsg, errmsg_plural(), ERROR, executor_errposition(), exprLocation(), fb(), fmgr_info_cxt(), fmgr_info_set_expr, FUNC_MAX_ARGS, get_expr_result_type(), get_func_name(), GetUserId(), InitFunctionCallInfoData, InvokeFunctionExecuteHook, list_length(), MemoryContextSwitchTo(), object_aclcheck(), OBJECT_FUNCTION, palloc(), SizeForFunctionCallInfo, PlanState::state, TupleDescFinalize(), 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 813 of file execSRF.c.

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

References arg, castNode, DatumGetPointer(), ExecClearTuple(), fb(), and tuplestore_end().

Referenced by ExecMakeFunctionResultSet(), and ExecPrepareTuplestoreResult().

◆ tupledesc_match()

static void tupledesc_match ( TupleDesc  dst_tupdesc,
TupleDesc  src_tupdesc 
)
static

Definition at line 946 of file execSRF.c.

947{
948 int i;
949
950 if (dst_tupdesc->natts != src_tupdesc->natts)
953 errmsg("function return row and query-specified return row do not match"),
954 errdetail_plural("Returned row contains %d attribute, but query expects %d.",
955 "Returned row contains %d attributes, but query expects %d.",
956 src_tupdesc->natts,
957 src_tupdesc->natts, dst_tupdesc->natts)));
958
959 for (i = 0; i < dst_tupdesc->natts; i++)
960 {
963
964 if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
965 continue; /* no worries */
966 if (!dattr->attisdropped)
969 errmsg("function return row and query-specified return row do not match"),
970 errdetail("Returned type %s at ordinal position %d, but query expects %s.",
971 format_type_be(sattr->atttypid),
972 i + 1,
973 format_type_be(dattr->atttypid))));
974
975 if (dattr->attlen != sattr->attlen ||
976 dattr->attalign != sattr->attalign)
979 errmsg("function return row and query-specified return row do not match"),
980 errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
981 i + 1)));
982 }
983}
int errdetail(const char *fmt,...) pg_attribute_printf(1
int errdetail_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...) pg_attribute_printf(1
char * format_type_be(Oid type_oid)
bool IsBinaryCoercible(Oid srctype, Oid targettype)
FormData_pg_attribute * Form_pg_attribute
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178

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

Referenced by ExecMakeTableFunctionResult(), and ExecPrepareTuplestoreResult().