LCOV - code coverage report
Current view: top level - src/backend/executor - nodeFunctionscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 156 159 98.1 %
Date: 2023-06-06 09:15:10 Functions: 5 6 83.3 %
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-2023, 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/builtins.h"
      30             : #include "utils/memutils.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    14776906 : 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    14776906 :     estate = node->ss.ps.state;
      74    14776906 :     direction = estate->es_direction;
      75    14776906 :     scanslot = node->ss.ss_ScanTupleSlot;
      76             : 
      77    14776906 :     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    14758146 :         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    14758146 :         if (tstore == NULL)
      93             :         {
      94      110934 :             node->funcstates[0].tstore = tstore =
      95      115996 :                 ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
      96             :                                             node->ss.ps.ps_ExprContext,
      97             :                                             node->argcontext,
      98      115996 :                                             node->funcstates[0].tupdesc,
      99      115996 :                                             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      110934 :             tuplestore_rescan(tstore);
     107             :         }
     108             : 
     109             :         /*
     110             :          * Get the next tuple from tuplestore.
     111             :          */
     112    14753084 :         (void) tuplestore_gettupleslot(tstore,
     113             :                                        ScanDirectionIsForward(direction),
     114             :                                        false,
     115             :                                        scanslot);
     116    14753084 :         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       18760 :     oldpos = node->ordinal;
     126       18760 :     if (ScanDirectionIsForward(direction))
     127       18700 :         node->ordinal++;
     128             :     else
     129          60 :         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       18760 :     ExecClearTuple(scanslot);
     139       18760 :     att = 0;
     140       18760 :     alldone = true;
     141       48450 :     for (funcno = 0; funcno < node->nfuncs; funcno++)
     142             :     {
     143       29690 :         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       29690 :         if (fs->tstore == NULL)
     152             :         {
     153        1608 :             fs->tstore =
     154        1608 :                 ExecMakeTableFunctionResult(fs->setexpr,
     155             :                                             node->ss.ps.ps_ExprContext,
     156             :                                             node->argcontext,
     157             :                                             fs->tupdesc,
     158        1608 :                                             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        1608 :             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       29690 :         if (fs->rowcount != -1 && fs->rowcount < oldpos)
     176          72 :             ExecClearTuple(fs->func_slot);
     177             :         else
     178       29618 :             (void) tuplestore_gettupleslot(fs->tstore,
     179             :                                            ScanDirectionIsForward(direction),
     180             :                                            false,
     181             :                                            fs->func_slot);
     182             : 
     183       29690 :         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        1956 :             if (ScanDirectionIsForward(direction) && fs->rowcount == -1)
     193        1584 :                 fs->rowcount = node->ordinal;
     194             : 
     195             :             /*
     196             :              * populate the result cols with nulls
     197             :              */
     198        4900 :             for (i = 0; i < fs->colcount; i++)
     199             :             {
     200        2944 :                 scanslot->tts_values[att] = (Datum) 0;
     201        2944 :                 scanslot->tts_isnull[att] = true;
     202        2944 :                 att++;
     203             :             }
     204             :         }
     205             :         else
     206             :         {
     207             :             /*
     208             :              * we have a result, so just copy it to the result cols.
     209             :              */
     210       27734 :             slot_getallattrs(fs->func_slot);
     211             : 
     212       73118 :             for (i = 0; i < fs->colcount; i++)
     213             :             {
     214       45384 :                 scanslot->tts_values[att] = fs->func_slot->tts_values[i];
     215       45384 :                 scanslot->tts_isnull[att] = fs->func_slot->tts_isnull[i];
     216       45384 :                 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       27734 :             alldone = false;
     224             :         }
     225             :     }
     226             : 
     227             :     /*
     228             :      * ordinal col is always last, per spec.
     229             :      */
     230       18760 :     if (node->ordinality)
     231             :     {
     232       13532 :         scanslot->tts_values[att] = Int64GetDatumFast(node->ordinal);
     233       13532 :         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       18760 :     if (!alldone)
     241       17440 :         ExecStoreVirtualTuple(scanslot);
     242             : 
     243       18760 :     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    13712640 : ExecFunctionScan(PlanState *pstate)
     267             : {
     268    13712640 :     FunctionScanState *node = castNode(FunctionScanState, pstate);
     269             : 
     270    13712640 :     return ExecScan(&node->ss,
     271             :                     (ExecScanAccessMtd) FunctionNext,
     272             :                     (ExecScanRecheckMtd) FunctionRecheck);
     273             : }
     274             : 
     275             : /* ----------------------------------------------------------------
     276             :  *      ExecInitFunctionScan
     277             :  * ----------------------------------------------------------------
     278             :  */
     279             : FunctionScanState *
     280       58206 : ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
     281             : {
     282             :     FunctionScanState *scanstate;
     283       58206 :     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       58206 :     scanstate = makeNode(FunctionScanState);
     302       58206 :     scanstate->ss.ps.plan = (Plan *) node;
     303       58206 :     scanstate->ss.ps.state = estate;
     304       58206 :     scanstate->ss.ps.ExecProcNode = ExecFunctionScan;
     305       58206 :     scanstate->eflags = eflags;
     306             : 
     307             :     /*
     308             :      * are we adding an ordinality column?
     309             :      */
     310       58206 :     scanstate->ordinality = node->funcordinality;
     311             : 
     312       58206 :     scanstate->nfuncs = nfuncs;
     313       58206 :     if (nfuncs == 1 && !node->funcordinality)
     314       57516 :         scanstate->simple = true;
     315             :     else
     316         690 :         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       58206 :     scanstate->ordinal = 0;
     329             : 
     330             :     /*
     331             :      * Miscellaneous initialization
     332             :      *
     333             :      * create expression context for node
     334             :      */
     335       58206 :     ExecAssignExprContext(estate, &scanstate->ss.ps);
     336             : 
     337       58206 :     scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
     338             : 
     339       58206 :     natts = 0;
     340       58206 :     i = 0;
     341      116716 :     foreach(lc, node->functions)
     342             :     {
     343       58518 :         RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
     344       58518 :         Node       *funcexpr = rtfunc->funcexpr;
     345       58518 :         int         colcount = rtfunc->funccolcount;
     346       58518 :         FunctionScanPerFuncState *fs = &scanstate->funcstates[i];
     347             :         TypeFuncClass functypclass;
     348             :         Oid         funcrettype;
     349             :         TupleDesc   tupdesc;
     350             : 
     351       58510 :         fs->setexpr =
     352       58518 :             ExecInitTableFunctionResult((Expr *) funcexpr,
     353             :                                         scanstate->ss.ps.ps_ExprContext,
     354             :                                         &scanstate->ss.ps);
     355             : 
     356             :         /*
     357             :          * Don't allocate the tuplestores; the actual calls to the functions
     358             :          * do that.  NULL means that we have not called the function yet (or
     359             :          * need to call it again after a rescan).
     360             :          */
     361       58510 :         fs->tstore = NULL;
     362       58510 :         fs->rowcount = -1;
     363             : 
     364             :         /*
     365             :          * Now determine if the function returns a simple or composite type,
     366             :          * and build an appropriate tupdesc.  Note that in the composite case,
     367             :          * the function may now return more columns than it did when the plan
     368             :          * was made; we have to ignore any columns beyond "colcount".
     369             :          */
     370       58510 :         functypclass = get_expr_result_type(funcexpr,
     371             :                                             &funcrettype,
     372             :                                             &tupdesc);
     373             : 
     374       58510 :         if (functypclass == TYPEFUNC_COMPOSITE ||
     375             :             functypclass == TYPEFUNC_COMPOSITE_DOMAIN)
     376             :         {
     377             :             /* Composite data type, e.g. a table's row type */
     378             :             Assert(tupdesc);
     379             :             Assert(tupdesc->natts >= colcount);
     380             :             /* Must copy it out of typcache for safety */
     381       36910 :             tupdesc = CreateTupleDescCopy(tupdesc);
     382             :         }
     383       21600 :         else if (functypclass == TYPEFUNC_SCALAR)
     384             :         {
     385             :             /* Base data type, i.e. scalar */
     386       20900 :             tupdesc = CreateTemplateTupleDesc(1);
     387       20900 :             TupleDescInitEntry(tupdesc,
     388             :                                (AttrNumber) 1,
     389             :                                NULL,    /* don't care about the name here */
     390             :                                funcrettype,
     391             :                                -1,
     392             :                                0);
     393       20900 :             TupleDescInitEntryCollation(tupdesc,
     394             :                                         (AttrNumber) 1,
     395             :                                         exprCollation(funcexpr));
     396             :         }
     397         700 :         else if (functypclass == TYPEFUNC_RECORD)
     398             :         {
     399         700 :             tupdesc = BuildDescFromLists(rtfunc->funccolnames,
     400             :                                          rtfunc->funccoltypes,
     401             :                                          rtfunc->funccoltypmods,
     402             :                                          rtfunc->funccolcollations);
     403             : 
     404             :             /*
     405             :              * For RECORD results, make sure a typmod has been assigned.  (The
     406             :              * function should do this for itself, but let's cover things in
     407             :              * case it doesn't.)
     408             :              */
     409         700 :             BlessTupleDesc(tupdesc);
     410             :         }
     411             :         else
     412             :         {
     413             :             /* crummy error message, but parser should have caught this */
     414           0 :             elog(ERROR, "function in FROM has unsupported return type");
     415             :         }
     416             : 
     417       58510 :         fs->tupdesc = tupdesc;
     418       58510 :         fs->colcount = colcount;
     419             : 
     420             :         /*
     421             :          * We only need separate slots for the function results if we are
     422             :          * doing ordinality or multiple functions; otherwise, we'll fetch
     423             :          * function results directly into the scan slot.
     424             :          */
     425       58510 :         if (!scanstate->simple)
     426             :         {
     427        1002 :             fs->func_slot = ExecInitExtraTupleSlot(estate, fs->tupdesc,
     428             :                                                    &TTSOpsMinimalTuple);
     429             :         }
     430             :         else
     431       57508 :             fs->func_slot = NULL;
     432             : 
     433       58510 :         natts += colcount;
     434       58510 :         i++;
     435             :     }
     436             : 
     437             :     /*
     438             :      * Create the combined TupleDesc
     439             :      *
     440             :      * If there is just one function without ordinality, the scan result
     441             :      * tupdesc is the same as the function result tupdesc --- except that we
     442             :      * may stuff new names into it below, so drop any rowtype label.
     443             :      */
     444       58198 :     if (scanstate->simple)
     445             :     {
     446       57508 :         scan_tupdesc = CreateTupleDescCopy(scanstate->funcstates[0].tupdesc);
     447       57508 :         scan_tupdesc->tdtypeid = RECORDOID;
     448       57508 :         scan_tupdesc->tdtypmod = -1;
     449             :     }
     450             :     else
     451             :     {
     452         690 :         AttrNumber  attno = 0;
     453             : 
     454         690 :         if (node->funcordinality)
     455         618 :             natts++;
     456             : 
     457         690 :         scan_tupdesc = CreateTemplateTupleDesc(natts);
     458             : 
     459        1692 :         for (i = 0; i < nfuncs; i++)
     460             :         {
     461        1002 :             TupleDesc   tupdesc = scanstate->funcstates[i].tupdesc;
     462        1002 :             int         colcount = scanstate->funcstates[i].colcount;
     463             :             int         j;
     464             : 
     465        2644 :             for (j = 1; j <= colcount; j++)
     466        1642 :                 TupleDescCopyEntry(scan_tupdesc, ++attno, tupdesc, j);
     467             :         }
     468             : 
     469             :         /* If doing ordinality, add a column of type "bigint" at the end */
     470         690 :         if (node->funcordinality)
     471             :         {
     472         618 :             TupleDescInitEntry(scan_tupdesc,
     473             :                                ++attno,
     474             :                                NULL,    /* don't care about the name here */
     475             :                                INT8OID,
     476             :                                -1,
     477             :                                0);
     478             :         }
     479             : 
     480             :         Assert(attno == natts);
     481             :     }
     482             : 
     483             :     /*
     484             :      * Initialize scan slot and type.
     485             :      */
     486       58198 :     ExecInitScanTupleSlot(estate, &scanstate->ss, scan_tupdesc,
     487             :                           &TTSOpsMinimalTuple);
     488             : 
     489             :     /*
     490             :      * Initialize result slot, type and projection.
     491             :      */
     492       58198 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     493       58198 :     ExecAssignScanProjectionInfo(&scanstate->ss);
     494             : 
     495             :     /*
     496             :      * initialize child expressions
     497             :      */
     498       58198 :     scanstate->ss.ps.qual =
     499       58198 :         ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
     500             : 
     501             :     /*
     502             :      * Create a memory context that ExecMakeTableFunctionResult can use to
     503             :      * evaluate function arguments in.  We can't use the per-tuple context for
     504             :      * this because it gets reset too often; but we don't want to leak
     505             :      * evaluation results into the query-lifespan context either.  We just
     506             :      * need one context, because we evaluate each function separately.
     507             :      */
     508       58198 :     scanstate->argcontext = AllocSetContextCreate(CurrentMemoryContext,
     509             :                                                   "Table function arguments",
     510             :                                                   ALLOCSET_DEFAULT_SIZES);
     511             : 
     512       58198 :     return scanstate;
     513             : }
     514             : 
     515             : /* ----------------------------------------------------------------
     516             :  *      ExecEndFunctionScan
     517             :  *
     518             :  *      frees any storage allocated through C routines.
     519             :  * ----------------------------------------------------------------
     520             :  */
     521             : void
     522       52970 : ExecEndFunctionScan(FunctionScanState *node)
     523             : {
     524             :     int         i;
     525             : 
     526             :     /*
     527             :      * Free the exprcontext
     528             :      */
     529       52970 :     ExecFreeExprContext(&node->ss.ps);
     530             : 
     531             :     /*
     532             :      * clean out the tuple table
     533             :      */
     534       52970 :     if (node->ss.ps.ps_ResultTupleSlot)
     535       19648 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     536       52970 :     ExecClearTuple(node->ss.ss_ScanTupleSlot);
     537             : 
     538             :     /*
     539             :      * Release slots and tuplestore resources
     540             :      */
     541      106240 :     for (i = 0; i < node->nfuncs; i++)
     542             :     {
     543       53270 :         FunctionScanPerFuncState *fs = &node->funcstates[i];
     544             : 
     545       53270 :         if (fs->func_slot)
     546         978 :             ExecClearTuple(fs->func_slot);
     547             : 
     548       53270 :         if (fs->tstore != NULL)
     549             :         {
     550       49304 :             tuplestore_end(node->funcstates[i].tstore);
     551       49304 :             fs->tstore = NULL;
     552             :         }
     553             :     }
     554       52970 : }
     555             : 
     556             : /* ----------------------------------------------------------------
     557             :  *      ExecReScanFunctionScan
     558             :  *
     559             :  *      Rescans the relation.
     560             :  * ----------------------------------------------------------------
     561             :  */
     562             : void
     563       77024 : ExecReScanFunctionScan(FunctionScanState *node)
     564             : {
     565       77024 :     FunctionScan *scan = (FunctionScan *) node->ss.ps.plan;
     566             :     int         i;
     567       77024 :     Bitmapset  *chgparam = node->ss.ps.chgParam;
     568             : 
     569       77024 :     if (node->ss.ps.ps_ResultTupleSlot)
     570       40406 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     571      154156 :     for (i = 0; i < node->nfuncs; i++)
     572             :     {
     573       77132 :         FunctionScanPerFuncState *fs = &node->funcstates[i];
     574             : 
     575       77132 :         if (fs->func_slot)
     576        1086 :             ExecClearTuple(fs->func_slot);
     577             :     }
     578             : 
     579       77024 :     ExecScanReScan(&node->ss);
     580             : 
     581             :     /*
     582             :      * Here we have a choice whether to drop the tuplestores (and recompute
     583             :      * the function outputs) or just rescan them.  We must recompute if an
     584             :      * expression contains changed parameters, else we rescan.
     585             :      *
     586             :      * XXX maybe we should recompute if the function is volatile?  But in
     587             :      * general the executor doesn't conditionalize its actions on that.
     588             :      */
     589       77024 :     if (chgparam)
     590             :     {
     591             :         ListCell   *lc;
     592             : 
     593       73794 :         i = 0;
     594      147672 :         foreach(lc, scan->functions)
     595             :         {
     596       73878 :             RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
     597             : 
     598       73878 :             if (bms_overlap(chgparam, rtfunc->funcparams))
     599             :             {
     600       71504 :                 if (node->funcstates[i].tstore != NULL)
     601             :                 {
     602       63060 :                     tuplestore_end(node->funcstates[i].tstore);
     603       63060 :                     node->funcstates[i].tstore = NULL;
     604             :                 }
     605       71504 :                 node->funcstates[i].rowcount = -1;
     606             :             }
     607       73878 :             i++;
     608             :         }
     609             :     }
     610             : 
     611             :     /* Reset ordinality counter */
     612       77024 :     node->ordinal = 0;
     613             : 
     614             :     /* Make sure we rewind any remaining tuplestores */
     615      154156 :     for (i = 0; i < node->nfuncs; i++)
     616             :     {
     617       77132 :         if (node->funcstates[i].tstore != NULL)
     618        5120 :             tuplestore_rescan(node->funcstates[i].tstore);
     619             :     }
     620       77024 : }

Generated by: LCOV version 1.14