LCOV - code coverage report
Current view: top level - src/backend/executor - nodeProjectSet.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 100.0 % 82 82
Test Date: 2026-03-01 02:14:51 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * nodeProjectSet.c
       4              :  *    support for evaluating targetlists containing set-returning functions
       5              :  *
       6              :  * DESCRIPTION
       7              :  *
       8              :  *      ProjectSet nodes are inserted by the planner to evaluate set-returning
       9              :  *      functions in the targetlist.  It's guaranteed that all set-returning
      10              :  *      functions are directly at the top level of the targetlist, i.e. they
      11              :  *      can't be inside more-complex expressions.  If that'd otherwise be
      12              :  *      the case, the planner adds additional ProjectSet nodes.
      13              :  *
      14              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      15              :  * Portions Copyright (c) 1994, Regents of the University of California
      16              :  *
      17              :  * IDENTIFICATION
      18              :  *    src/backend/executor/nodeProjectSet.c
      19              :  *
      20              :  *-------------------------------------------------------------------------
      21              :  */
      22              : 
      23              : #include "postgres.h"
      24              : 
      25              : #include "executor/executor.h"
      26              : #include "executor/nodeProjectSet.h"
      27              : #include "miscadmin.h"
      28              : #include "nodes/nodeFuncs.h"
      29              : 
      30              : 
      31              : static TupleTableSlot *ExecProjectSRF(ProjectSetState *node, bool continuing);
      32              : 
      33              : 
      34              : /* ----------------------------------------------------------------
      35              :  *      ExecProjectSet(node)
      36              :  *
      37              :  *      Return tuples after evaluating the targetlist (which contains set
      38              :  *      returning functions).
      39              :  * ----------------------------------------------------------------
      40              :  */
      41              : static TupleTableSlot *
      42      1505197 : ExecProjectSet(PlanState *pstate)
      43              : {
      44      1505197 :     ProjectSetState *node = castNode(ProjectSetState, pstate);
      45              :     TupleTableSlot *outerTupleSlot;
      46              :     TupleTableSlot *resultSlot;
      47              :     PlanState  *outerPlan;
      48              :     ExprContext *econtext;
      49              : 
      50      1505197 :     CHECK_FOR_INTERRUPTS();
      51              : 
      52      1505197 :     econtext = node->ps.ps_ExprContext;
      53              : 
      54              :     /*
      55              :      * Reset per-tuple context to free expression-evaluation storage allocated
      56              :      * for a potentially previously returned tuple. Note that the SRF argument
      57              :      * context has a different lifetime and is reset below.
      58              :      */
      59      1505197 :     ResetExprContext(econtext);
      60              : 
      61              :     /*
      62              :      * Check to see if we're still projecting out tuples from a previous scan
      63              :      * tuple (because there is a function-returning-set in the projection
      64              :      * expressions).  If so, try to project another one.
      65              :      */
      66      1505197 :     if (node->pending_srf_tuples)
      67              :     {
      68      1486290 :         resultSlot = ExecProjectSRF(node, true);
      69              : 
      70      1486290 :         if (resultSlot != NULL)
      71      1410974 :             return resultSlot;
      72              :     }
      73              : 
      74              :     /*
      75              :      * Get another input tuple and project SRFs from it.
      76              :      */
      77              :     for (;;)
      78              :     {
      79              :         /*
      80              :          * Reset argument context to free any expression evaluation storage
      81              :          * allocated in the previous tuple cycle.  Note this can't happen
      82              :          * until we're done projecting out tuples from a scan tuple, as
      83              :          * ValuePerCall functions are allowed to reference the arguments for
      84              :          * each returned tuple.  However, if we loop around after finding that
      85              :          * no rows are produced from a scan tuple, we should reset, to avoid
      86              :          * leaking memory when many successive scan tuples produce no rows.
      87              :          */
      88       138943 :         MemoryContextReset(node->argcontext);
      89              : 
      90              :         /*
      91              :          * Retrieve tuples from the outer plan until there are no more.
      92              :          */
      93       138943 :         outerPlan = outerPlanState(node);
      94       138943 :         outerTupleSlot = ExecProcNode(outerPlan);
      95              : 
      96       138943 :         if (TupIsNull(outerTupleSlot))
      97        18095 :             return NULL;
      98              : 
      99              :         /*
     100              :          * Prepare to compute projection expressions, which will expect to
     101              :          * access the input tuples as varno OUTER.
     102              :          */
     103       120848 :         econtext->ecxt_outertuple = outerTupleSlot;
     104              : 
     105              :         /* Evaluate the expressions */
     106       120848 :         resultSlot = ExecProjectSRF(node, false);
     107              : 
     108              :         /*
     109              :          * Return the tuple unless the projection produced no rows (due to an
     110              :          * empty set), in which case we must loop back to see if there are
     111              :          * more outerPlan tuples.
     112              :          */
     113       120092 :         if (resultSlot)
     114        75372 :             return resultSlot;
     115              : 
     116              :         /*
     117              :          * When we do loop back, we'd better reset the econtext again, just in
     118              :          * case the SRF leaked some memory there.
     119              :          */
     120        44720 :         ResetExprContext(econtext);
     121              :     }
     122              : 
     123              :     return NULL;
     124              : }
     125              : 
     126              : /* ----------------------------------------------------------------
     127              :  *      ExecProjectSRF
     128              :  *
     129              :  *      Project a targetlist containing one or more set-returning functions.
     130              :  *
     131              :  *      'continuing' indicates whether to continue projecting rows for the
     132              :  *      same input tuple; or whether a new input tuple is being projected.
     133              :  *
     134              :  *      Returns NULL if no output tuple has been produced.
     135              :  *
     136              :  * ----------------------------------------------------------------
     137              :  */
     138              : static TupleTableSlot *
     139      1607138 : ExecProjectSRF(ProjectSetState *node, bool continuing)
     140              : {
     141      1607138 :     TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
     142      1607138 :     ExprContext *econtext = node->ps.ps_ExprContext;
     143              :     MemoryContext oldcontext;
     144              :     bool        hassrf PG_USED_FOR_ASSERTS_ONLY;
     145              :     bool        hasresult;
     146              :     int         argno;
     147              : 
     148      1607138 :     ExecClearTuple(resultSlot);
     149              : 
     150              :     /* Call SRFs, as well as plain expressions, in per-tuple context */
     151      1607138 :     oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
     152              : 
     153              :     /*
     154              :      * Assume no further tuples are produced unless an ExprMultipleResult is
     155              :      * encountered from a set returning function.
     156              :      */
     157      1607138 :     node->pending_srf_tuples = false;
     158              : 
     159      1607138 :     hassrf = hasresult = false;
     160      4406789 :     for (argno = 0; argno < node->nelems; argno++)
     161              :     {
     162      2800407 :         Node       *elem = node->elems[argno];
     163      2800407 :         ExprDoneCond *isdone = &node->elemdone[argno];
     164      2800407 :         Datum      *result = &resultSlot->tts_values[argno];
     165      2800407 :         bool       *isnull = &resultSlot->tts_isnull[argno];
     166              : 
     167      2800407 :         if (continuing && *isdone == ExprEndResult)
     168              :         {
     169              :             /*
     170              :              * If we're continuing to project output rows from a source tuple,
     171              :              * return NULLs once the SRF has been exhausted.
     172              :              */
     173        15016 :             *result = (Datum) 0;
     174        15016 :             *isnull = true;
     175        15016 :             hassrf = true;
     176              :         }
     177      2785391 :         else if (IsA(elem, SetExprState))
     178              :         {
     179              :             /*
     180              :              * Evaluate SRF - possibly continuing previously started output.
     181              :              */
     182      1692780 :             *result = ExecMakeFunctionResultSet((SetExprState *) elem,
     183              :                                                 econtext, node->argcontext,
     184              :                                                 isnull, isdone);
     185              : 
     186      1692024 :             if (*isdone != ExprEndResult)
     187      1569082 :                 hasresult = true;
     188      1692024 :             if (*isdone == ExprMultipleResult)
     189      1569079 :                 node->pending_srf_tuples = true;
     190      1692024 :             hassrf = true;
     191              :         }
     192              :         else
     193              :         {
     194              :             /* Non-SRF tlist expression, just evaluate normally. */
     195      1092611 :             *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
     196      1092611 :             *isdone = ExprSingleResult;
     197              :         }
     198              :     }
     199              : 
     200      1606382 :     MemoryContextSwitchTo(oldcontext);
     201              : 
     202              :     /* ProjectSet should not be used if there's no SRFs */
     203              :     Assert(hassrf);
     204              : 
     205              :     /*
     206              :      * If all the SRFs returned ExprEndResult, we consider that as no row
     207              :      * being produced.
     208              :      */
     209      1606382 :     if (hasresult)
     210              :     {
     211      1486346 :         ExecStoreVirtualTuple(resultSlot);
     212      1486346 :         return resultSlot;
     213              :     }
     214              : 
     215       120036 :     return NULL;
     216              : }
     217              : 
     218              : /* ----------------------------------------------------------------
     219              :  *      ExecInitProjectSet
     220              :  *
     221              :  *      Creates the run-time state information for the ProjectSet node
     222              :  *      produced by the planner and initializes outer relations
     223              :  *      (child nodes).
     224              :  * ----------------------------------------------------------------
     225              :  */
     226              : ProjectSetState *
     227         7362 : ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
     228              : {
     229              :     ProjectSetState *state;
     230              :     ListCell   *lc;
     231              :     int         off;
     232              : 
     233              :     /* check for unsupported flags */
     234              :     Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
     235              : 
     236              :     /*
     237              :      * create state structure
     238              :      */
     239         7362 :     state = makeNode(ProjectSetState);
     240         7362 :     state->ps.plan = (Plan *) node;
     241         7362 :     state->ps.state = estate;
     242         7362 :     state->ps.ExecProcNode = ExecProjectSet;
     243              : 
     244         7362 :     state->pending_srf_tuples = false;
     245              : 
     246              :     /*
     247              :      * Miscellaneous initialization
     248              :      *
     249              :      * create expression context for node
     250              :      */
     251         7362 :     ExecAssignExprContext(estate, &state->ps);
     252              : 
     253              :     /*
     254              :      * initialize child nodes
     255              :      */
     256         7362 :     outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
     257              : 
     258              :     /*
     259              :      * we don't use inner plan
     260              :      */
     261              :     Assert(innerPlan(node) == NULL);
     262              : 
     263              :     /*
     264              :      * tuple table and result type initialization
     265              :      */
     266         7362 :     ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual);
     267              : 
     268              :     /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
     269         7362 :     state->nelems = list_length(node->plan.targetlist);
     270         7362 :     state->elems = palloc_array(Node *, state->nelems);
     271         7362 :     state->elemdone = palloc_array(ExprDoneCond, state->nelems);
     272              : 
     273              :     /*
     274              :      * Build expressions to evaluate targetlist.  We can't use
     275              :      * ExecBuildProjectionInfo here, since that doesn't deal with SRFs.
     276              :      * Instead compile each expression separately, using
     277              :      * ExecInitFunctionResultSet where applicable.
     278              :      */
     279         7362 :     off = 0;
     280        15736 :     foreach(lc, node->plan.targetlist)
     281              :     {
     282         8375 :         TargetEntry *te = (TargetEntry *) lfirst(lc);
     283         8375 :         Expr       *expr = te->expr;
     284              : 
     285         8375 :         if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
     286          921 :             (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
     287              :         {
     288         7456 :             state->elems[off] = (Node *)
     289         7457 :                 ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
     290              :                                           &state->ps);
     291              :         }
     292              :         else
     293              :         {
     294              :             Assert(!expression_returns_set((Node *) expr));
     295          918 :             state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
     296              :         }
     297              : 
     298         8374 :         off++;
     299              :     }
     300              : 
     301              :     /* We don't support any qual on ProjectSet nodes */
     302              :     Assert(node->plan.qual == NIL);
     303              : 
     304              :     /*
     305              :      * Create a memory context that ExecMakeFunctionResultSet can use to
     306              :      * evaluate function arguments in.  We can't use the per-tuple context for
     307              :      * this because it gets reset too often; but we don't want to leak
     308              :      * evaluation results into the query-lifespan context either.  We use one
     309              :      * context for the arguments of all tSRFs, as they have roughly equivalent
     310              :      * lifetimes.
     311              :      */
     312         7361 :     state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
     313              :                                               "tSRF function arguments",
     314              :                                               ALLOCSET_DEFAULT_SIZES);
     315              : 
     316         7361 :     return state;
     317              : }
     318              : 
     319              : /* ----------------------------------------------------------------
     320              :  *      ExecEndProjectSet
     321              :  *
     322              :  *      frees up storage allocated through C routines
     323              :  * ----------------------------------------------------------------
     324              :  */
     325              : void
     326         6601 : ExecEndProjectSet(ProjectSetState *node)
     327              : {
     328              :     /*
     329              :      * shut down subplans
     330              :      */
     331         6601 :     ExecEndNode(outerPlanState(node));
     332         6601 : }
     333              : 
     334              : void
     335        12970 : ExecReScanProjectSet(ProjectSetState *node)
     336              : {
     337        12970 :     PlanState  *outerPlan = outerPlanState(node);
     338              : 
     339              :     /* Forget any incompletely-evaluated SRFs */
     340        12970 :     node->pending_srf_tuples = false;
     341              : 
     342              :     /*
     343              :      * If chgParam of subnode is not null then plan will be re-scanned by
     344              :      * first ExecProcNode.
     345              :      */
     346        12970 :     if (outerPlan->chgParam == NULL)
     347        12970 :         ExecReScan(outerPlan);
     348        12970 : }
        

Generated by: LCOV version 2.0-1