LCOV - code coverage report
Current view: top level - src/backend/executor - nodeFunctionscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 98.1 % 158 155
Test Date: 2026-03-21 08:16:18 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * nodeFunctionscan.c
       4              :  *    Support routines for scanning RangeFunctions (functions in rangetable).
       5              :  *
       6              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/backend/executor/nodeFunctionscan.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : /*
      16              :  * INTERFACE ROUTINES
      17              :  *      ExecFunctionScan        scans a function.
      18              :  *      ExecFunctionNext        retrieve next tuple in sequential order.
      19              :  *      ExecInitFunctionScan    creates and initializes a functionscan node.
      20              :  *      ExecEndFunctionScan     releases any storage allocated.
      21              :  *      ExecReScanFunctionScan  rescans the function
      22              :  */
      23              : #include "postgres.h"
      24              : 
      25              : #include "catalog/pg_type.h"
      26              : #include "executor/nodeFunctionscan.h"
      27              : #include "funcapi.h"
      28              : #include "nodes/nodeFuncs.h"
      29              : #include "utils/memutils.h"
      30              : #include "utils/tuplestore.h"
      31              : 
      32              : 
      33              : /*
      34              :  * Runtime data for each function being scanned.
      35              :  */
      36              : typedef struct FunctionScanPerFuncState
      37              : {
      38              :     SetExprState *setexpr;      /* state of the expression being evaluated */
      39              :     TupleDesc   tupdesc;        /* desc of the function result type */
      40              :     int         colcount;       /* expected number of result columns */
      41              :     Tuplestorestate *tstore;    /* holds the function result set */
      42              :     int64       rowcount;       /* # of rows in result set, -1 if not known */
      43              :     TupleTableSlot *func_slot;  /* function result slot (or NULL) */
      44              : } FunctionScanPerFuncState;
      45              : 
      46              : static TupleTableSlot *FunctionNext(FunctionScanState *node);
      47              : 
      48              : 
      49              : /* ----------------------------------------------------------------
      50              :  *                      Scan Support
      51              :  * ----------------------------------------------------------------
      52              :  */
      53              : /* ----------------------------------------------------------------
      54              :  *      FunctionNext
      55              :  *
      56              :  *      This is a workhorse for ExecFunctionScan
      57              :  * ----------------------------------------------------------------
      58              :  */
      59              : static TupleTableSlot *
      60     10620797 : FunctionNext(FunctionScanState *node)
      61              : {
      62              :     EState     *estate;
      63              :     ScanDirection direction;
      64              :     TupleTableSlot *scanslot;
      65              :     bool        alldone;
      66              :     int64       oldpos;
      67              :     int         funcno;
      68              :     int         att;
      69              : 
      70              :     /*
      71              :      * get information from the estate and scan state
      72              :      */
      73     10620797 :     estate = node->ss.ps.state;
      74     10620797 :     direction = estate->es_direction;
      75     10620797 :     scanslot = node->ss.ss_ScanTupleSlot;
      76              : 
      77     10620797 :     if (node->simple)
      78              :     {
      79              :         /*
      80              :          * Fast path for the trivial case: the function return type and scan
      81              :          * result type are the same, so we fetch the function result straight
      82              :          * into the scan result slot. No need to update ordinality or
      83              :          * rowcounts either.
      84              :          */
      85     10606236 :         Tuplestorestate *tstore = node->funcstates[0].tstore;
      86              : 
      87              :         /*
      88              :          * If first time through, read all tuples from function and put them
      89              :          * in a tuplestore. Subsequent calls just fetch tuples from
      90              :          * tuplestore.
      91              :          */
      92     10606236 :         if (tstore == NULL)
      93              :         {
      94        96588 :             node->funcstates[0].tstore = tstore =
      95        99967 :                 ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
      96              :                                             node->ss.ps.ps_ExprContext,
      97              :                                             node->argcontext,
      98        99967 :                                             node->funcstates[0].tupdesc,
      99        99967 :                                             node->eflags & EXEC_FLAG_BACKWARD);
     100              : 
     101              :             /*
     102              :              * paranoia - cope if the function, which may have constructed the
     103              :              * tuplestore itself, didn't leave it pointing at the start. This
     104              :              * call is fast, so the overhead shouldn't be an issue.
     105              :              */
     106        96588 :             tuplestore_rescan(tstore);
     107              :         }
     108              : 
     109              :         /*
     110              :          * Get the next tuple from tuplestore.
     111              :          */
     112     10602857 :         (void) tuplestore_gettupleslot(tstore,
     113              :                                        ScanDirectionIsForward(direction),
     114              :                                        false,
     115              :                                        scanslot);
     116     10602857 :         return scanslot;
     117              :     }
     118              : 
     119              :     /*
     120              :      * Increment or decrement ordinal counter before checking for end-of-data,
     121              :      * so that we can move off either end of the result by 1 (and no more than
     122              :      * 1) without losing correct count.  See PortalRunSelect for why we can
     123              :      * assume that we won't be called repeatedly in the end-of-data state.
     124              :      */
     125        14561 :     oldpos = node->ordinal;
     126        14561 :     if (ScanDirectionIsForward(direction))
     127        14521 :         node->ordinal++;
     128              :     else
     129           40 :         node->ordinal--;
     130              : 
     131              :     /*
     132              :      * Main loop over functions.
     133              :      *
     134              :      * We fetch the function results into func_slots (which match the function
     135              :      * return types), and then copy the values to scanslot (which matches the
     136              :      * scan result type), setting the ordinal column (if any) as well.
     137              :      */
     138        14561 :     ExecClearTuple(scanslot);
     139        14561 :     att = 0;
     140        14561 :     alldone = true;
     141        34700 :     for (funcno = 0; funcno < node->nfuncs; funcno++)
     142              :     {
     143        20139 :         FunctionScanPerFuncState *fs = &node->funcstates[funcno];
     144              :         int         i;
     145              : 
     146              :         /*
     147              :          * If first time through, read all tuples from function and put them
     148              :          * in a tuplestore. Subsequent calls just fetch tuples from
     149              :          * tuplestore.
     150              :          */
     151        20139 :         if (fs->tstore == NULL)
     152              :         {
     153         1514 :             fs->tstore =
     154         1514 :                 ExecMakeTableFunctionResult(fs->setexpr,
     155              :                                             node->ss.ps.ps_ExprContext,
     156              :                                             node->argcontext,
     157              :                                             fs->tupdesc,
     158         1514 :                                             node->eflags & EXEC_FLAG_BACKWARD);
     159              : 
     160              :             /*
     161              :              * paranoia - cope if the function, which may have constructed the
     162              :              * tuplestore itself, didn't leave it pointing at the start. This
     163              :              * call is fast, so the overhead shouldn't be an issue.
     164              :              */
     165         1514 :             tuplestore_rescan(fs->tstore);
     166              :         }
     167              : 
     168              :         /*
     169              :          * Get the next tuple from tuplestore.
     170              :          *
     171              :          * If we have a rowcount for the function, and we know the previous
     172              :          * read position was out of bounds, don't try the read. This allows
     173              :          * backward scan to work when there are mixed row counts present.
     174              :          */
     175        20139 :         if (fs->rowcount != -1 && fs->rowcount < oldpos)
     176           50 :             ExecClearTuple(fs->func_slot);
     177              :         else
     178        20089 :             (void) tuplestore_gettupleslot(fs->tstore,
     179              :                                            ScanDirectionIsForward(direction),
     180              :                                            false,
     181              :                                            fs->func_slot);
     182              : 
     183        20139 :         if (TupIsNull(fs->func_slot))
     184              :         {
     185              :             /*
     186              :              * If we ran out of data for this function in the forward
     187              :              * direction then we now know how many rows it returned. We need
     188              :              * to know this in order to handle backwards scans. The row count
     189              :              * we store is actually 1+ the actual number, because we have to
     190              :              * position the tuplestore 1 off its end sometimes.
     191              :              */
     192         1733 :             if (ScanDirectionIsForward(direction) && fs->rowcount == -1)
     193         1482 :                 fs->rowcount = node->ordinal;
     194              : 
     195              :             /*
     196              :              * populate the result cols with nulls
     197              :              */
     198         4124 :             for (i = 0; i < fs->colcount; i++)
     199              :             {
     200         2391 :                 scanslot->tts_values[att] = (Datum) 0;
     201         2391 :                 scanslot->tts_isnull[att] = true;
     202         2391 :                 att++;
     203              :             }
     204              :         }
     205              :         else
     206              :         {
     207              :             /*
     208              :              * we have a result, so just copy it to the result cols.
     209              :              */
     210        18406 :             slot_getallattrs(fs->func_slot);
     211              : 
     212        45910 :             for (i = 0; i < fs->colcount; i++)
     213              :             {
     214        27504 :                 scanslot->tts_values[att] = fs->func_slot->tts_values[i];
     215        27504 :                 scanslot->tts_isnull[att] = fs->func_slot->tts_isnull[i];
     216        27504 :                 att++;
     217              :             }
     218              : 
     219              :             /*
     220              :              * We're not done until every function result is exhausted; we pad
     221              :              * the shorter results with nulls until then.
     222              :              */
     223        18406 :             alldone = false;
     224              :         }
     225              :     }
     226              : 
     227              :     /*
     228              :      * ordinal col is always last, per spec.
     229              :      */
     230        14561 :     if (node->ordinality)
     231              :     {
     232        11923 :         scanslot->tts_values[att] = Int64GetDatumFast(node->ordinal);
     233        11923 :         scanslot->tts_isnull[att] = false;
     234              :     }
     235              : 
     236              :     /*
     237              :      * If alldone, we just return the previously-cleared scanslot.  Otherwise,
     238              :      * finish creating the virtual tuple.
     239              :      */
     240        14561 :     if (!alldone)
     241        13252 :         ExecStoreVirtualTuple(scanslot);
     242              : 
     243        14561 :     return scanslot;
     244              : }
     245              : 
     246              : /*
     247              :  * FunctionRecheck -- access method routine to recheck a tuple in EvalPlanQual
     248              :  */
     249              : static bool
     250            0 : FunctionRecheck(FunctionScanState *node, TupleTableSlot *slot)
     251              : {
     252              :     /* nothing to check */
     253            0 :     return true;
     254              : }
     255              : 
     256              : /* ----------------------------------------------------------------
     257              :  *      ExecFunctionScan(node)
     258              :  *
     259              :  *      Scans the function sequentially and returns the next qualifying
     260              :  *      tuple.
     261              :  *      We call the ExecScan() routine and pass it the appropriate
     262              :  *      access method functions.
     263              :  * ----------------------------------------------------------------
     264              :  */
     265              : static TupleTableSlot *
     266      9465826 : ExecFunctionScan(PlanState *pstate)
     267              : {
     268      9465826 :     FunctionScanState *node = castNode(FunctionScanState, pstate);
     269              : 
     270      9465826 :     return ExecScan(&node->ss,
     271              :                     (ExecScanAccessMtd) FunctionNext,
     272              :                     (ExecScanRecheckMtd) FunctionRecheck);
     273              : }
     274              : 
     275              : /* ----------------------------------------------------------------
     276              :  *      ExecInitFunctionScan
     277              :  * ----------------------------------------------------------------
     278              :  */
     279              : FunctionScanState *
     280        43913 : ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
     281              : {
     282              :     FunctionScanState *scanstate;
     283        43913 :     int         nfuncs = list_length(node->functions);
     284              :     TupleDesc   scan_tupdesc;
     285              :     int         i,
     286              :                 natts;
     287              :     ListCell   *lc;
     288              : 
     289              :     /* check for unsupported flags */
     290              :     Assert(!(eflags & EXEC_FLAG_MARK));
     291              : 
     292              :     /*
     293              :      * FunctionScan should not have any children.
     294              :      */
     295              :     Assert(outerPlan(node) == NULL);
     296              :     Assert(innerPlan(node) == NULL);
     297              : 
     298              :     /*
     299              :      * create new ScanState for node
     300              :      */
     301        43913 :     scanstate = makeNode(FunctionScanState);
     302        43913 :     scanstate->ss.ps.plan = (Plan *) node;
     303        43913 :     scanstate->ss.ps.state = estate;
     304        43913 :     scanstate->ss.ps.ExecProcNode = ExecFunctionScan;
     305        43913 :     scanstate->eflags = eflags;
     306              : 
     307              :     /*
     308              :      * are we adding an ordinality column?
     309              :      */
     310        43913 :     scanstate->ordinality = node->funcordinality;
     311              : 
     312        43913 :     scanstate->nfuncs = nfuncs;
     313        43913 :     if (nfuncs == 1 && !node->funcordinality)
     314        42951 :         scanstate->simple = true;
     315              :     else
     316          962 :         scanstate->simple = false;
     317              : 
     318              :     /*
     319              :      * Ordinal 0 represents the "before the first row" position.
     320              :      *
     321              :      * We need to track ordinal position even when not adding an ordinality
     322              :      * column to the result, in order to handle backwards scanning properly
     323              :      * with multiple functions with different result sizes. (We can't position
     324              :      * any individual function's tuplestore any more than 1 place beyond its
     325              :      * end, so when scanning backwards, we need to know when to start
     326              :      * including the function in the scan again.)
     327              :      */
     328        43913 :     scanstate->ordinal = 0;
     329              : 
     330              :     /*
     331              :      * Miscellaneous initialization
     332              :      *
     333              :      * create expression context for node
     334              :      */
     335        43913 :     ExecAssignExprContext(estate, &scanstate->ss.ps);
     336              : 
     337        43913 :     scanstate->funcstates = palloc_array(FunctionScanPerFuncState, nfuncs);
     338              : 
     339        43913 :     natts = 0;
     340        43913 :     i = 0;
     341        88027 :     foreach(lc, node->functions)
     342              :     {
     343        44118 :         RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
     344        44118 :         Node       *funcexpr = rtfunc->funcexpr;
     345        44118 :         int         colcount = rtfunc->funccolcount;
     346        44118 :         FunctionScanPerFuncState *fs = &scanstate->funcstates[i];
     347              :         TupleDesc   tupdesc;
     348              : 
     349        44114 :         fs->setexpr =
     350        44118 :             ExecInitTableFunctionResult((Expr *) funcexpr,
     351              :                                         scanstate->ss.ps.ps_ExprContext,
     352              :                                         &scanstate->ss.ps);
     353              : 
     354              :         /*
     355              :          * Don't allocate the tuplestores; the actual calls to the functions
     356              :          * do that.  NULL means that we have not called the function yet (or
     357              :          * need to call it again after a rescan).
     358              :          */
     359        44114 :         fs->tstore = NULL;
     360        44114 :         fs->rowcount = -1;
     361              : 
     362              :         /*
     363              :          * Now build a tupdesc showing the result type we expect from the
     364              :          * function.  If we have a coldeflist then that takes priority (note
     365              :          * the parser enforces that there is one if the function's nominal
     366              :          * output type is RECORD).  Otherwise use get_expr_result_type.
     367              :          *
     368              :          * Note that if the function returns a named composite type, that may
     369              :          * now contain more or different columns than it did when the plan was
     370              :          * made.  For both that and the RECORD case, we need to check tuple
     371              :          * compatibility.  ExecMakeTableFunctionResult handles some of this,
     372              :          * and CheckVarSlotCompatibility provides a backstop.
     373              :          */
     374        44114 :         if (rtfunc->funccolnames != NIL)
     375              :         {
     376          934 :             tupdesc = BuildDescFromLists(rtfunc->funccolnames,
     377          467 :                                          rtfunc->funccoltypes,
     378          467 :                                          rtfunc->funccoltypmods,
     379          467 :                                          rtfunc->funccolcollations);
     380              : 
     381              :             /*
     382              :              * For RECORD results, make sure a typmod has been assigned.  (The
     383              :              * function should do this for itself, but let's cover things in
     384              :              * case it doesn't.)
     385              :              */
     386          467 :             BlessTupleDesc(tupdesc);
     387              :         }
     388              :         else
     389              :         {
     390              :             TypeFuncClass functypclass;
     391              :             Oid         funcrettype;
     392              : 
     393        43647 :             functypclass = get_expr_result_type(funcexpr,
     394              :                                                 &funcrettype,
     395              :                                                 &tupdesc);
     396              : 
     397        43647 :             if (functypclass == TYPEFUNC_COMPOSITE ||
     398              :                 functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
     399              :             {
     400              :                 /* Composite data type, e.g. a table's row type */
     401              :                 Assert(tupdesc);
     402              :                 /* Must copy it out of typcache for safety */
     403        23800 :                 tupdesc = CreateTupleDescCopy(tupdesc);
     404              :             }
     405        19847 :             else if (functypclass == TYPEFUNC_SCALAR)
     406              :             {
     407              :                 /* Base data type, i.e. scalar */
     408        19847 :                 tupdesc = CreateTemplateTupleDesc(1);
     409        19847 :                 TupleDescInitEntry(tupdesc,
     410              :                                    (AttrNumber) 1,
     411              :                                    NULL,    /* don't care about the name here */
     412              :                                    funcrettype,
     413              :                                    -1,
     414              :                                    0);
     415        19847 :                 TupleDescInitEntryCollation(tupdesc,
     416              :                                             (AttrNumber) 1,
     417              :                                             exprCollation(funcexpr));
     418        19847 :                 TupleDescFinalize(tupdesc);
     419              :             }
     420              :             else
     421              :             {
     422              :                 /* crummy error message, but parser should have caught this */
     423            0 :                 elog(ERROR, "function in FROM has unsupported return type");
     424              :             }
     425              :         }
     426              : 
     427        44114 :         fs->tupdesc = tupdesc;
     428        44114 :         fs->colcount = colcount;
     429              : 
     430              :         /*
     431              :          * We only need separate slots for the function results if we are
     432              :          * doing ordinality or multiple functions; otherwise, we'll fetch
     433              :          * function results directly into the scan slot.
     434              :          */
     435        44114 :         if (!scanstate->simple)
     436              :         {
     437         1167 :             fs->func_slot = ExecInitExtraTupleSlot(estate, fs->tupdesc,
     438              :                                                    &TTSOpsMinimalTuple);
     439              :         }
     440              :         else
     441        42947 :             fs->func_slot = NULL;
     442              : 
     443        44114 :         natts += colcount;
     444        44114 :         i++;
     445              :     }
     446              : 
     447              :     /*
     448              :      * Create the combined TupleDesc
     449              :      *
     450              :      * If there is just one function without ordinality, the scan result
     451              :      * tupdesc is the same as the function result tupdesc --- except that we
     452              :      * may stuff new names into it below, so drop any rowtype label.
     453              :      */
     454        43909 :     if (scanstate->simple)
     455              :     {
     456        42947 :         scan_tupdesc = CreateTupleDescCopy(scanstate->funcstates[0].tupdesc);
     457        42947 :         scan_tupdesc->tdtypeid = RECORDOID;
     458        42947 :         scan_tupdesc->tdtypmod = -1;
     459              :     }
     460              :     else
     461              :     {
     462          962 :         AttrNumber  attno = 0;
     463              : 
     464          962 :         if (node->funcordinality)
     465          915 :             natts++;
     466              : 
     467          962 :         scan_tupdesc = CreateTemplateTupleDesc(natts);
     468              : 
     469         2129 :         for (i = 0; i < nfuncs; i++)
     470              :         {
     471         1167 :             TupleDesc   tupdesc = scanstate->funcstates[i].tupdesc;
     472         1167 :             int         colcount = scanstate->funcstates[i].colcount;
     473              :             int         j;
     474              : 
     475         2760 :             for (j = 1; j <= colcount; j++)
     476         1593 :                 TupleDescCopyEntry(scan_tupdesc, ++attno, tupdesc, j);
     477              :         }
     478              : 
     479              :         /* If doing ordinality, add a column of type "bigint" at the end */
     480          962 :         if (node->funcordinality)
     481              :         {
     482          915 :             TupleDescInitEntry(scan_tupdesc,
     483              :                                ++attno,
     484              :                                NULL,    /* don't care about the name here */
     485              :                                INT8OID,
     486              :                                -1,
     487              :                                0);
     488              :         }
     489              : 
     490          962 :         TupleDescFinalize(scan_tupdesc);
     491              :         Assert(attno == natts);
     492              :     }
     493              : 
     494              :     /*
     495              :      * Initialize scan slot and type.
     496              :      */
     497        43909 :     ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc,
     498              :                           &TTSOpsMinimalTuple, 0);
     499              : 
     500              :     /*
     501              :      * Initialize result slot, type and projection.
     502              :      */
     503        43909 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     504        43909 :     ExecAssignScanProjectionInfo(&scanstate->ss);
     505              : 
     506              :     /*
     507              :      * initialize child expressions
     508              :      */
     509        43909 :     scanstate->ss.ps.qual =
     510        43909 :         ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
     511              : 
     512              :     /*
     513              :      * Create a memory context that ExecMakeTableFunctionResult can use to
     514              :      * evaluate function arguments in.  We can't use the per-tuple context for
     515              :      * this because it gets reset too often; but we don't want to leak
     516              :      * evaluation results into the query-lifespan context either.  We just
     517              :      * need one context, because we evaluate each function separately.
     518              :      */
     519        43909 :     scanstate->argcontext = AllocSetContextCreate(CurrentMemoryContext,
     520              :                                                   "Table function arguments",
     521              :                                                   ALLOCSET_DEFAULT_SIZES);
     522              : 
     523        43909 :     return scanstate;
     524              : }
     525              : 
     526              : /* ----------------------------------------------------------------
     527              :  *      ExecEndFunctionScan
     528              :  *
     529              :  *      frees any storage allocated through C routines.
     530              :  * ----------------------------------------------------------------
     531              :  */
     532              : void
     533        40411 : ExecEndFunctionScan(FunctionScanState *node)
     534              : {
     535              :     int         i;
     536              : 
     537              :     /*
     538              :      * Release slots and tuplestore resources
     539              :      */
     540        81019 :     for (i = 0; i < node->nfuncs; i++)
     541              :     {
     542        40608 :         FunctionScanPerFuncState *fs = &node->funcstates[i];
     543              : 
     544        40608 :         if (fs->tstore != NULL)
     545              :         {
     546        36031 :             tuplestore_end(node->funcstates[i].tstore);
     547        36031 :             fs->tstore = NULL;
     548              :         }
     549              :     }
     550        40411 : }
     551              : 
     552              : /* ----------------------------------------------------------------
     553              :  *      ExecReScanFunctionScan
     554              :  *
     555              :  *      Rescans the relation.
     556              :  * ----------------------------------------------------------------
     557              :  */
     558              : void
     559        77304 : ExecReScanFunctionScan(FunctionScanState *node)
     560              : {
     561        77304 :     FunctionScan *scan = (FunctionScan *) node->ss.ps.plan;
     562              :     int         i;
     563        77304 :     Bitmapset  *chgparam = node->ss.ps.chgParam;
     564              : 
     565        77304 :     if (node->ss.ps.ps_ResultTupleSlot)
     566        31809 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     567       154681 :     for (i = 0; i < node->nfuncs; i++)
     568              :     {
     569        77377 :         FunctionScanPerFuncState *fs = &node->funcstates[i];
     570              : 
     571        77377 :         if (fs->func_slot)
     572          742 :             ExecClearTuple(fs->func_slot);
     573              :     }
     574              : 
     575        77304 :     ExecScanReScan(&node->ss);
     576              : 
     577              :     /*
     578              :      * Here we have a choice whether to drop the tuplestores (and recompute
     579              :      * the function outputs) or just rescan them.  We must recompute if an
     580              :      * expression contains changed parameters, else we rescan.
     581              :      *
     582              :      * XXX maybe we should recompute if the function is volatile?  But in
     583              :      * general the executor doesn't conditionalize its actions on that.
     584              :      */
     585        77304 :     if (chgparam)
     586              :     {
     587              :         ListCell   *lc;
     588              : 
     589        71678 :         i = 0;
     590       143413 :         foreach(lc, scan->functions)
     591              :         {
     592        71735 :             RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
     593              : 
     594        71735 :             if (bms_overlap(chgparam, rtfunc->funcparams))
     595              :             {
     596        68739 :                 if (node->funcstates[i].tstore != NULL)
     597              :                 {
     598        61944 :                     tuplestore_end(node->funcstates[i].tstore);
     599        61944 :                     node->funcstates[i].tstore = NULL;
     600              :                 }
     601        68739 :                 node->funcstates[i].rowcount = -1;
     602              :             }
     603        71735 :             i++;
     604              :         }
     605              :     }
     606              : 
     607              :     /* Reset ordinality counter */
     608        77304 :     node->ordinal = 0;
     609              : 
     610              :     /* Make sure we rewind any remaining tuplestores */
     611       154681 :     for (i = 0; i < node->nfuncs; i++)
     612              :     {
     613        77377 :         if (node->funcstates[i].tstore != NULL)
     614         8058 :             tuplestore_rescan(node->funcstates[i].tstore);
     615              :     }
     616        77304 : }
        

Generated by: LCOV version 2.0-1