LCOV - code coverage report
Current view: top level - src/backend/executor - execSRF.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.1 % 288 274
Test Date: 2026-03-17 04:14:51 Functions: 100.0 % 9 9
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * execSRF.c
       4              :  *    Routines implementing the API for set-returning functions
       5              :  *
       6              :  * This file serves nodeFunctionscan.c and nodeProjectSet.c, providing
       7              :  * common code for calling set-returning functions according to the
       8              :  * ReturnSetInfo API.
       9              :  *
      10              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      11              :  * Portions Copyright (c) 1994, Regents of the University of California
      12              :  *
      13              :  *
      14              :  * IDENTIFICATION
      15              :  *    src/backend/executor/execSRF.c
      16              :  *
      17              :  *-------------------------------------------------------------------------
      18              :  */
      19              : #include "postgres.h"
      20              : 
      21              : #include "access/htup_details.h"
      22              : #include "catalog/objectaccess.h"
      23              : #include "catalog/pg_proc.h"
      24              : #include "funcapi.h"
      25              : #include "miscadmin.h"
      26              : #include "nodes/nodeFuncs.h"
      27              : #include "parser/parse_coerce.h"
      28              : #include "pgstat.h"
      29              : #include "utils/acl.h"
      30              : #include "utils/builtins.h"
      31              : #include "utils/lsyscache.h"
      32              : #include "utils/memutils.h"
      33              : #include "utils/tuplestore.h"
      34              : #include "utils/typcache.h"
      35              : 
      36              : 
      37              : /* static function decls */
      38              : static void init_sexpr(Oid foid, Oid input_collation, Expr *node,
      39              :                        SetExprState *sexpr, PlanState *parent,
      40              :                        MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF);
      41              : static void ShutdownSetExpr(Datum arg);
      42              : static void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
      43              :                              List *argList, ExprContext *econtext);
      44              : static void ExecPrepareTuplestoreResult(SetExprState *sexpr,
      45              :                                         ExprContext *econtext,
      46              :                                         Tuplestorestate *resultStore,
      47              :                                         TupleDesc resultDesc);
      48              : static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
      49              : 
      50              : 
      51              : /*
      52              :  * Prepare function call in FROM (ROWS FROM) for execution.
      53              :  *
      54              :  * This is used by nodeFunctionscan.c.
      55              :  */
      56              : SetExprState *
      57        38301 : ExecInitTableFunctionResult(Expr *expr,
      58              :                             ExprContext *econtext, PlanState *parent)
      59              : {
      60        38301 :     SetExprState *state = makeNode(SetExprState);
      61              : 
      62        38301 :     state->funcReturnsSet = false;
      63        38301 :     state->expr = expr;
      64        38301 :     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        38301 :     if (IsA(expr, FuncExpr))
      76              :     {
      77        38244 :         FuncExpr   *func = (FuncExpr *) expr;
      78              : 
      79        38244 :         state->funcReturnsSet = func->funcretset;
      80        38244 :         state->args = ExecInitExprList(func->args, parent);
      81              : 
      82        38244 :         init_sexpr(func->funcid, func->inputcollid, expr, state, parent,
      83        38244 :                    econtext->ecxt_per_query_memory, func->funcretset, false);
      84              :     }
      85              :     else
      86              :     {
      87           57 :         state->elidedFuncState = ExecInitExpr(expr, parent);
      88              :     }
      89              : 
      90        38297 :     return state;
      91              : }
      92              : 
      93              : /*
      94              :  *      ExecMakeTableFunctionResult
      95              :  *
      96              :  * Evaluate a table function, producing a materialized result in a Tuplestore
      97              :  * object.
      98              :  *
      99              :  * This is used by nodeFunctionscan.c.
     100              :  */
     101              : Tuplestorestate *
     102        93787 : ExecMakeTableFunctionResult(SetExprState *setexpr,
     103              :                             ExprContext *econtext,
     104              :                             MemoryContext argContext,
     105              :                             TupleDesc expectedDesc,
     106              :                             bool randomAccess)
     107              : {
     108        93787 :     Tuplestorestate *tupstore = NULL;
     109        93787 :     TupleDesc   tupdesc = NULL;
     110              :     Oid         funcrettype;
     111              :     bool        returnsTuple;
     112        93787 :     bool        returnsSet = false;
     113              :     FunctionCallInfo fcinfo;
     114              :     PgStat_FunctionCallUsage fcusage;
     115              :     ReturnSetInfo rsinfo;
     116              :     HeapTupleData tmptup;
     117              :     MemoryContext callerContext;
     118        93787 :     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        93787 :     MemoryContextReset(argContext);
     134        93787 :     callerContext = MemoryContextSwitchTo(argContext);
     135              : 
     136        93787 :     funcrettype = exprType((Node *) setexpr->expr);
     137              : 
     138        93787 :     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        93787 :     rsinfo.type = T_ReturnSetInfo;
     148        93787 :     rsinfo.econtext = econtext;
     149        93787 :     rsinfo.expectedDesc = expectedDesc;
     150        93787 :     rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
     151        93787 :     if (randomAccess)
     152           40 :         rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
     153        93787 :     rsinfo.returnMode = SFRM_ValuePerCall;
     154              :     /* isDone is filled below */
     155        93787 :     rsinfo.setResult = NULL;
     156        93787 :     rsinfo.setDesc = NULL;
     157              : 
     158        93787 :     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        93787 :     if (!setexpr->elidedFuncState)
     171              :     {
     172              :         /*
     173              :          * This path is similar to ExecMakeFunctionResultSet.
     174              :          */
     175        93730 :         returnsSet = setexpr->funcReturnsSet;
     176        93730 :         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        93730 :         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        93722 :         if (setexpr->func.fn_strict)
     190              :         {
     191              :             int         i;
     192              : 
     193       174855 :             for (i = 0; i < fcinfo->nargs; i++)
     194              :             {
     195       119648 :                 if (fcinfo->args[i].isnull)
     196        27307 :                     goto no_function_result;
     197              :             }
     198              :         }
     199              :     }
     200              :     else
     201              :     {
     202              :         /* Treat setexpr as a generic expression */
     203           57 :         InitFunctionCallInfoData(*fcinfo, NULL, 0, InvalidOid, NULL, NULL);
     204              :     }
     205              : 
     206              :     /*
     207              :      * Switch to short-lived context for calling the function or expression.
     208              :      */
     209        66472 :     MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     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     11032050 :     {
     217              :         Datum       result;
     218              : 
     219     11098522 :         CHECK_FOR_INTERRUPTS();
     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     11098521 :         ResetExprContext(econtext);
     227              : 
     228              :         /* Call the function or expression one time */
     229     11098521 :         if (!setexpr->elidedFuncState)
     230              :         {
     231     11098464 :             pgstat_init_function_usage(fcinfo, &fcusage);
     232              : 
     233     11098464 :             fcinfo->isnull = false;
     234     11098464 :             rsinfo.isDone = ExprSingleResult;
     235     11098464 :             result = FunctionCallInvoke(fcinfo);
     236              : 
     237     11095897 :             pgstat_end_function_usage(&fcusage,
     238     11095897 :                                       rsinfo.isDone != ExprMultipleResult);
     239              :         }
     240              :         else
     241              :         {
     242           57 :             result =
     243           57 :                 ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo->isnull);
     244           57 :             rsinfo.isDone = ExprSingleResult;
     245              :         }
     246              : 
     247              :         /* Which protocol does function want to use? */
     248     11095954 :         if (rsinfo.returnMode == SFRM_ValuePerCall)
     249              :         {
     250              :             /*
     251              :              * Check for end of result set.
     252              :              */
     253     11085889 :             if (rsinfo.isDone == ExprEndResult)
     254        63904 :                 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     11053521 :             if (first_time)
     261              :             {
     262              :                 MemoryContext oldcontext =
     263        52811 :                     MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
     264              : 
     265        52811 :                 tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
     266        52811 :                 rsinfo.setResult = tupstore;
     267        52811 :                 if (!returnsTuple)
     268              :                 {
     269        25883 :                     tupdesc = CreateTemplateTupleDesc(1);
     270        25883 :                     TupleDescInitEntry(tupdesc,
     271              :                                        (AttrNumber) 1,
     272              :                                        "column",
     273              :                                        funcrettype,
     274              :                                        -1,
     275              :                                        0);
     276        25883 :                     TupleDescFinalize(tupdesc);
     277        25883 :                     rsinfo.setDesc = tupdesc;
     278              :                 }
     279        52811 :                 MemoryContextSwitchTo(oldcontext);
     280              :             }
     281              : 
     282              :             /*
     283              :              * Store current resultset item.
     284              :              */
     285     11053521 :             if (returnsTuple)
     286              :             {
     287      1059004 :                 if (!fcinfo->isnull)
     288              :                 {
     289      1058964 :                     HeapTupleHeader td = DatumGetHeapTupleHeader(result);
     290              : 
     291      1058964 :                     if (tupdesc == NULL)
     292              :                     {
     293              :                         MemoryContext oldcontext =
     294        26900 :                             MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
     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              :                          */
     302        26900 :                         tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
     303              :                                                               HeapTupleHeaderGetTypMod(td));
     304        26900 :                         rsinfo.setDesc = tupdesc;
     305        26900 :                         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      1032064 :                         if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
     314      1032064 :                             HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod)
     315            0 :                             ereport(ERROR,
     316              :                                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     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              :                      */
     324      1058964 :                     tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
     325      1058964 :                     tmptup.t_data = td;
     326              : 
     327      1058964 :                     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           40 :                     int         natts = expectedDesc->natts;
     340              :                     bool       *nullflags;
     341              : 
     342           40 :                     nullflags = (bool *) palloc(natts * sizeof(bool));
     343           40 :                     memset(nullflags, true, natts * sizeof(bool));
     344           40 :                     tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
     345              :                 }
     346              :             }
     347              :             else
     348              :             {
     349              :                 /* Scalar-type case: just store the function result */
     350      9994517 :                 tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo->isnull);
     351              :             }
     352              : 
     353              :             /*
     354              :              * Are we done?
     355              :              */
     356     11053521 :             if (rsinfo.isDone != ExprMultipleResult)
     357        21471 :                 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     11032050 :             if (!returnsSet)
     365            0 :                 ereport(ERROR,
     366              :                         (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
     367              :                          errmsg("table-function protocol for value-per-call mode was not followed")));
     368              :         }
     369        10065 :         else if (rsinfo.returnMode == SFRM_Materialize)
     370              :         {
     371              :             /* check we're on the same page as the function author */
     372        10065 :             if (!first_time || rsinfo.isDone != ExprSingleResult || !returnsSet)
     373            0 :                 ereport(ERROR,
     374              :                         (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
     375              :                          errmsg("table-function protocol for materialize mode was not followed")));
     376              :             /* Done evaluating the set result */
     377        10065 :             break;
     378              :         }
     379              :         else
     380            0 :             ereport(ERROR,
     381              :                     (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
     382              :                      errmsg("unrecognized table-function returnMode: %d",
     383              :                             (int) rsinfo.returnMode)));
     384              : 
     385     11032050 :         first_time = false;
     386              :     }
     387              : 
     388        91211 : no_function_result:
     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        91211 :     if (rsinfo.setResult == NULL)
     397              :     {
     398              :         MemoryContext oldcontext =
     399        28352 :             MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
     400              : 
     401        28352 :         tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
     402        28352 :         rsinfo.setResult = tupstore;
     403        28352 :         MemoryContextSwitchTo(oldcontext);
     404              : 
     405        28352 :         if (!returnsSet)
     406              :         {
     407           11 :             int         natts = expectedDesc->natts;
     408              :             bool       *nullflags;
     409              : 
     410           11 :             nullflags = (bool *) palloc(natts * sizeof(bool));
     411           11 :             memset(nullflags, true, natts * sizeof(bool));
     412           11 :             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        91211 :     if (rsinfo.setDesc)
     421              :     {
     422        62831 :         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        62789 :         if (rsinfo.setDesc->tdrefcount == -1)
     430        62789 :             FreeTupleDesc(rsinfo.setDesc);
     431              :     }
     432              : 
     433        91169 :     MemoryContextSwitchTo(callerContext);
     434              : 
     435              :     /* All done, pass back the tuplestore */
     436        91169 :     return rsinfo.setResult;
     437              : }
     438              : 
     439              : 
     440              : /*
     441              :  * Prepare targetlist SRF function call for execution.
     442              :  *
     443              :  * This is used by nodeProjectSet.c.
     444              :  */
     445              : SetExprState *
     446         7928 : ExecInitFunctionResultSet(Expr *expr,
     447              :                           ExprContext *econtext, PlanState *parent)
     448              : {
     449         7928 :     SetExprState *state = makeNode(SetExprState);
     450              : 
     451         7928 :     state->funcReturnsSet = true;
     452         7928 :     state->expr = expr;
     453         7928 :     state->func.fn_oid = InvalidOid;
     454              : 
     455              :     /*
     456              :      * Initialize metadata.  The expression node could be either a FuncExpr or
     457              :      * an OpExpr.
     458              :      */
     459         7928 :     if (IsA(expr, FuncExpr))
     460              :     {
     461         7925 :         FuncExpr   *func = (FuncExpr *) expr;
     462              : 
     463         7925 :         state->args = ExecInitExprList(func->args, parent);
     464         7925 :         init_sexpr(func->funcid, func->inputcollid, expr, state, parent,
     465              :                    econtext->ecxt_per_query_memory, true, true);
     466              :     }
     467            3 :     else if (IsA(expr, OpExpr))
     468              :     {
     469            3 :         OpExpr     *op = (OpExpr *) expr;
     470              : 
     471            3 :         state->args = ExecInitExprList(op->args, parent);
     472            3 :         init_sexpr(op->opfuncid, op->inputcollid, expr, state, parent,
     473              :                    econtext->ecxt_per_query_memory, true, true);
     474              :     }
     475              :     else
     476            0 :         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         7927 :     return state;
     483              : }
     484              : 
     485              : /*
     486              :  *      ExecMakeFunctionResultSet
     487              :  *
     488              :  * Evaluate the arguments to a set-returning function and then call the
     489              :  * function itself.  The argument expressions may not contain set-returning
     490              :  * functions (the planner is supposed to have separated evaluation for those).
     491              :  *
     492              :  * This should be called in a short-lived (per-tuple) context, argContext
     493              :  * needs to live until all rows have been returned (i.e. *isDone set to
     494              :  * ExprEndResult or ExprSingleResult).
     495              :  *
     496              :  * This is used by nodeProjectSet.c.
     497              :  */
     498              : Datum
     499      1727163 : ExecMakeFunctionResultSet(SetExprState *fcache,
     500              :                           ExprContext *econtext,
     501              :                           MemoryContext argContext,
     502              :                           bool *isNull,
     503              :                           ExprDoneCond *isDone)
     504              : {
     505              :     List       *arguments;
     506              :     Datum       result;
     507              :     FunctionCallInfo fcinfo;
     508              :     PgStat_FunctionCallUsage fcusage;
     509              :     ReturnSetInfo rsinfo;
     510              :     bool        callit;
     511              :     int         i;
     512              : 
     513      1733828 : restart:
     514              : 
     515              :     /* Guard against stack overflow due to overly complex expressions */
     516      1733828 :     check_stack_depth();
     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      1733828 :     if (fcache->funcResultStore)
     524              :     {
     525        39150 :         TupleTableSlot *slot = fcache->funcResultSlot;
     526              :         MemoryContext oldContext;
     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              :          */
     534        39150 :         oldContext = MemoryContextSwitchTo(slot->tts_mcxt);
     535        39150 :         foundTup = tuplestore_gettupleslot(fcache->funcResultStore, true, false,
     536              :                                            fcache->funcResultSlot);
     537        39150 :         MemoryContextSwitchTo(oldContext);
     538              : 
     539        39150 :         if (foundTup)
     540              :         {
     541        32503 :             *isDone = ExprMultipleResult;
     542        32503 :             if (fcache->funcReturnsTuple)
     543              :             {
     544              :                 /* We must return the whole tuple as a Datum. */
     545        29467 :                 *isNull = false;
     546        29467 :                 return ExecFetchSlotHeapTupleDatum(fcache->funcResultSlot);
     547              :             }
     548              :             else
     549              :             {
     550              :                 /* Extract the first column and return it as a scalar. */
     551         3036 :                 return slot_getattr(fcache->funcResultSlot, 1, isNull);
     552              :             }
     553              :         }
     554              :         /* Exhausted the tuplestore, so clean up */
     555         6647 :         tuplestore_end(fcache->funcResultStore);
     556         6647 :         fcache->funcResultStore = NULL;
     557         6647 :         *isDone = ExprEndResult;
     558         6647 :         *isNull = true;
     559         6647 :         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      1694678 :     fcinfo = fcache->fcinfo;
     573      1694678 :     arguments = fcache->args;
     574      1694678 :     if (!fcache->setArgsValid)
     575              :     {
     576       136182 :         MemoryContext oldContext = MemoryContextSwitchTo(argContext);
     577              : 
     578       136182 :         ExecEvalFuncArgs(fcinfo, arguments, econtext);
     579       136182 :         MemoryContextSwitchTo(oldContext);
     580              :     }
     581              :     else
     582              :     {
     583              :         /* Reset flag (we may set it again below) */
     584      1558496 :         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      1694678 :     fcinfo->resultinfo = (Node *) &rsinfo;
     593      1694678 :     rsinfo.type = T_ReturnSetInfo;
     594      1694678 :     rsinfo.econtext = econtext;
     595      1694678 :     rsinfo.expectedDesc = fcache->funcResultDesc;
     596      1694678 :     rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
     597              :     /* note we do not set SFRM_Materialize_Random or _Preferred */
     598      1694678 :     rsinfo.returnMode = SFRM_ValuePerCall;
     599              :     /* isDone is filled below */
     600      1694678 :     rsinfo.setResult = NULL;
     601      1694678 :     rsinfo.setDesc = NULL;
     602              : 
     603              :     /*
     604              :      * If function is strict, and there are any NULL arguments, skip calling
     605              :      * the function.
     606              :      */
     607      1694678 :     callit = true;
     608      1694678 :     if (fcache->func.fn_strict)
     609              :     {
     610      4729476 :         for (i = 0; i < fcinfo->nargs; i++)
     611              :         {
     612      3070892 :             if (fcinfo->args[i].isnull)
     613              :             {
     614        33755 :                 callit = false;
     615        33755 :                 break;
     616              :             }
     617              :         }
     618              :     }
     619              : 
     620      1694678 :     if (callit)
     621              :     {
     622      1660923 :         pgstat_init_function_usage(fcinfo, &fcusage);
     623              : 
     624      1660923 :         fcinfo->isnull = false;
     625      1660923 :         rsinfo.isDone = ExprSingleResult;
     626      1660923 :         result = FunctionCallInvoke(fcinfo);
     627      1660167 :         *isNull = fcinfo->isnull;
     628      1660167 :         *isDone = rsinfo.isDone;
     629              : 
     630      1660167 :         pgstat_end_function_usage(&fcusage,
     631      1660167 :                                   rsinfo.isDone != ExprMultipleResult);
     632              :     }
     633              :     else
     634              :     {
     635              :         /* for a strict SRF, result for NULL is an empty set */
     636        33755 :         result = (Datum) 0;
     637        33755 :         *isNull = true;
     638        33755 :         *isDone = ExprEndResult;
     639              :     }
     640              : 
     641              :     /* Which protocol does function want to use? */
     642      1693922 :     if (rsinfo.returnMode == SFRM_ValuePerCall)
     643              :     {
     644      1687245 :         if (*isDone != ExprEndResult)
     645              :         {
     646              :             /*
     647              :              * Save the current argument values to re-use on the next call.
     648              :              */
     649      1558536 :             if (*isDone == ExprMultipleResult)
     650              :             {
     651      1558533 :                 fcache->setArgsValid = true;
     652              :                 /* Register cleanup callback if we didn't already */
     653      1558533 :                 if (!fcache->shutdown_reg)
     654              :                 {
     655        15361 :                     RegisterExprContextCallback(econtext,
     656              :                                                 ShutdownSetExpr,
     657              :                                                 PointerGetDatum(fcache));
     658        15361 :                     fcache->shutdown_reg = true;
     659              :                 }
     660              :             }
     661              :         }
     662              :     }
     663         6677 :     else if (rsinfo.returnMode == SFRM_Materialize)
     664              :     {
     665              :         /* check we're on the same page as the function author */
     666         6677 :         if (rsinfo.isDone != ExprSingleResult)
     667            0 :             ereport(ERROR,
     668              :                     (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
     669              :                      errmsg("table-function protocol for materialize mode was not followed")));
     670         6677 :         if (rsinfo.setResult != NULL)
     671              :         {
     672              :             /* prepare to return values from the tuplestore */
     673         6665 :             ExecPrepareTuplestoreResult(fcache, econtext,
     674              :                                         rsinfo.setResult,
     675              :                                         rsinfo.setDesc);
     676              :             /* loop back to top to start returning from tuplestore */
     677         6665 :             goto restart;
     678              :         }
     679              :         /* if setResult was left null, treat it as empty set */
     680           12 :         *isDone = ExprEndResult;
     681           12 :         *isNull = true;
     682           12 :         result = (Datum) 0;
     683              :     }
     684              :     else
     685            0 :         ereport(ERROR,
     686              :                 (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
     687              :                  errmsg("unrecognized table-function returnMode: %d",
     688              :                         (int) rsinfo.returnMode)));
     689              : 
     690      1687257 :     return result;
     691              : }
     692              : 
     693              : 
     694              : /*
     695              :  * init_sexpr - initialize a SetExprState node during first use
     696              :  */
     697              : static void
     698        46172 : init_sexpr(Oid foid, Oid input_collation, Expr *node,
     699              :            SetExprState *sexpr, PlanState *parent,
     700              :            MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
     701              : {
     702              :     AclResult   aclresult;
     703        46172 :     size_t      numargs = list_length(sexpr->args);
     704              : 
     705              :     /* Check permission to call function */
     706        46172 :     aclresult = object_aclcheck(ProcedureRelationId, foid, GetUserId(), ACL_EXECUTE);
     707        46172 :     if (aclresult != ACLCHECK_OK)
     708            5 :         aclcheck_error(aclresult, OBJECT_FUNCTION, get_func_name(foid));
     709        46167 :     InvokeFunctionExecuteHook(foid);
     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        46167 :     if (list_length(sexpr->args) > FUNC_MAX_ARGS)
     718            0 :         ereport(ERROR,
     719              :                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
     720              :                  errmsg_plural("cannot pass more than %d argument to a function",
     721              :                                "cannot pass more than %d arguments to a function",
     722              :                                FUNC_MAX_ARGS,
     723              :                                FUNC_MAX_ARGS)));
     724              : 
     725              :     /* Set up the primary fmgr lookup information */
     726        46167 :     fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
     727        46167 :     fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
     728              : 
     729              :     /* Initialize the function call parameter struct as well */
     730        46167 :     sexpr->fcinfo =
     731        46167 :         (FunctionCallInfo) palloc(SizeForFunctionCallInfo(numargs));
     732        46167 :     InitFunctionCallInfoData(*sexpr->fcinfo, &(sexpr->func),
     733              :                              numargs,
     734              :                              input_collation, NULL, NULL);
     735              : 
     736              :     /* If function returns set, check if that's allowed by caller */
     737        46167 :     if (sexpr->func.fn_retset && !allowSRF)
     738            0 :         ereport(ERROR,
     739              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     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        46167 :     if (sexpr->func.fn_retset && needDescForSRF)
     749         7927 :     {
     750              :         TypeFuncClass functypclass;
     751              :         Oid         funcrettype;
     752              :         TupleDesc   tupdesc;
     753              :         MemoryContext oldcontext;
     754              : 
     755         7927 :         functypclass = get_expr_result_type(sexpr->func.fn_expr,
     756              :                                             &funcrettype,
     757              :                                             &tupdesc);
     758              : 
     759              :         /* Must save tupdesc in sexpr's context */
     760         7927 :         oldcontext = MemoryContextSwitchTo(sexprCxt);
     761              : 
     762         7927 :         if (functypclass == TYPEFUNC_COMPOSITE ||
     763              :             functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
     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          669 :             sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc);
     769          669 :             sexpr->funcReturnsTuple = true;
     770              :         }
     771         7258 :         else if (functypclass == TYPEFUNC_SCALAR)
     772              :         {
     773              :             /* Base data type, i.e. scalar */
     774         7216 :             tupdesc = CreateTemplateTupleDesc(1);
     775         7216 :             TupleDescInitEntry(tupdesc,
     776              :                                (AttrNumber) 1,
     777              :                                NULL,
     778              :                                funcrettype,
     779              :                                -1,
     780              :                                0);
     781         7216 :             TupleDescFinalize(tupdesc);
     782         7216 :             sexpr->funcResultDesc = tupdesc;
     783         7216 :             sexpr->funcReturnsTuple = false;
     784              :         }
     785           42 :         else if (functypclass == TYPEFUNC_RECORD)
     786              :         {
     787              :             /* This will work if function doesn't need an expectedDesc */
     788           42 :             sexpr->funcResultDesc = NULL;
     789           42 :             sexpr->funcReturnsTuple = true;
     790              :         }
     791              :         else
     792              :         {
     793              :             /* Else, we will fail if function needs an expectedDesc */
     794            0 :             sexpr->funcResultDesc = NULL;
     795              :         }
     796              : 
     797         7927 :         MemoryContextSwitchTo(oldcontext);
     798              :     }
     799              :     else
     800        38240 :         sexpr->funcResultDesc = NULL;
     801              : 
     802              :     /* Initialize additional state */
     803        46167 :     sexpr->funcResultStore = NULL;
     804        46167 :     sexpr->funcResultSlot = NULL;
     805        46167 :     sexpr->shutdown_reg = false;
     806        46167 : }
     807              : 
     808              : /*
     809              :  * callback function in case a SetExprState needs to be shut down before it
     810              :  * has been run to completion
     811              :  */
     812              : static void
     813        15949 : ShutdownSetExpr(Datum arg)
     814              : {
     815        15949 :     SetExprState *sexpr = castNode(SetExprState, DatumGetPointer(arg));
     816              : 
     817              :     /* If we have a slot, make sure it's let go of any tuplestore pointer */
     818        15949 :     if (sexpr->funcResultSlot)
     819          592 :         ExecClearTuple(sexpr->funcResultSlot);
     820              : 
     821              :     /* Release any open tuplestore */
     822        15949 :     if (sexpr->funcResultStore)
     823           18 :         tuplestore_end(sexpr->funcResultStore);
     824        15949 :     sexpr->funcResultStore = NULL;
     825              : 
     826              :     /* Clear any active set-argument state */
     827        15949 :     sexpr->setArgsValid = false;
     828              : 
     829              :     /* execUtils will deregister the callback... */
     830        15949 :     sexpr->shutdown_reg = false;
     831        15949 : }
     832              : 
     833              : /*
     834              :  * Evaluate arguments for a function.
     835              :  */
     836              : static void
     837       229912 : ExecEvalFuncArgs(FunctionCallInfo fcinfo,
     838              :                  List *argList,
     839              :                  ExprContext *econtext)
     840              : {
     841              :     int         i;
     842              :     ListCell   *arg;
     843              : 
     844       229912 :     i = 0;
     845       581331 :     foreach(arg, argList)
     846              :     {
     847       351427 :         ExprState  *argstate = (ExprState *) lfirst(arg);
     848              : 
     849       351427 :         fcinfo->args[i].value = ExecEvalExpr(argstate,
     850              :                                              econtext,
     851              :                                              &fcinfo->args[i].isnull);
     852       351419 :         i++;
     853              :     }
     854              : 
     855              :     Assert(i == fcinfo->nargs);
     856       229904 : }
     857              : 
     858              : /*
     859              :  *      ExecPrepareTuplestoreResult
     860              :  *
     861              :  * Subroutine for ExecMakeFunctionResultSet: prepare to extract rows from a
     862              :  * tuplestore function result.  We must set up a funcResultSlot (unless
     863              :  * already done in a previous call cycle) and verify that the function
     864              :  * returned the expected tuple descriptor.
     865              :  */
     866              : static void
     867         6665 : ExecPrepareTuplestoreResult(SetExprState *sexpr,
     868              :                             ExprContext *econtext,
     869              :                             Tuplestorestate *resultStore,
     870              :                             TupleDesc resultDesc)
     871              : {
     872         6665 :     sexpr->funcResultStore = resultStore;
     873              : 
     874         6665 :     if (sexpr->funcResultSlot == NULL)
     875              :     {
     876              :         /* Create a slot so we can read data out of the tuplestore */
     877              :         TupleDesc   slotDesc;
     878              :         MemoryContext oldcontext;
     879              : 
     880          592 :         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          592 :         if (sexpr->funcResultDesc)
     887          571 :             slotDesc = sexpr->funcResultDesc;
     888           21 :         else if (resultDesc)
     889              :         {
     890              :             /* don't assume resultDesc is long-lived */
     891           21 :             slotDesc = CreateTupleDescCopy(resultDesc);
     892              :         }
     893              :         else
     894              :         {
     895            0 :             ereport(ERROR,
     896              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     897              :                      errmsg("function returning setof record called in "
     898              :                             "context that cannot accept type record")));
     899              :             slotDesc = NULL;    /* keep compiler quiet */
     900              :         }
     901              : 
     902          592 :         sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc,
     903              :                                                          &TTSOpsMinimalTuple);
     904          592 :         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         6665 :     if (resultDesc)
     912              :     {
     913         6665 :         if (sexpr->funcResultDesc)
     914         6638 :             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         6665 :         if (resultDesc->tdrefcount == -1)
     922         6665 :             FreeTupleDesc(resultDesc);
     923              :     }
     924              : 
     925              :     /* Register cleanup callback if we didn't already */
     926         6665 :     if (!sexpr->shutdown_reg)
     927              :     {
     928          592 :         RegisterExprContextCallback(econtext,
     929              :                                     ShutdownSetExpr,
     930              :                                     PointerGetDatum(sexpr));
     931          592 :         sexpr->shutdown_reg = true;
     932              :     }
     933         6665 : }
     934              : 
     935              : /*
     936              :  * Check that function result tuple type (src_tupdesc) matches or can
     937              :  * be considered to match what the query expects (dst_tupdesc). If
     938              :  * they don't match, ereport.
     939              :  *
     940              :  * We really only care about number of attributes and data type.
     941              :  * Also, we can ignore type mismatch on columns that are dropped in the
     942              :  * destination type, so long as the physical storage matches.  This is
     943              :  * helpful in some cases involving out-of-date cached plans.
     944              :  */
     945              : static void
     946        69469 : tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
     947              : {
     948              :     int         i;
     949              : 
     950        69469 :     if (dst_tupdesc->natts != src_tupdesc->natts)
     951           24 :         ereport(ERROR,
     952              :                 (errcode(ERRCODE_DATATYPE_MISMATCH),
     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       321069 :     for (i = 0; i < dst_tupdesc->natts; i++)
     960              :     {
     961       251642 :         Form_pg_attribute dattr = TupleDescAttr(dst_tupdesc, i);
     962       251642 :         Form_pg_attribute sattr = TupleDescAttr(src_tupdesc, i);
     963              : 
     964       251642 :         if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
     965       251624 :             continue;           /* no worries */
     966           18 :         if (!dattr->attisdropped)
     967           18 :             ereport(ERROR,
     968              :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     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            0 :         if (dattr->attlen != sattr->attlen ||
     976            0 :             dattr->attalign != sattr->attalign)
     977            0 :             ereport(ERROR,
     978              :                     (errcode(ERRCODE_DATATYPE_MISMATCH),
     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        69427 : }
        

Generated by: LCOV version 2.0-1