LCOV - code coverage report
Current view: top level - src/backend/executor - nodeProjectSet.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 84 84 100.0 %
Date: 2024-04-19 20:11:07 Functions: 5 5 100.0 %
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-2024, 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     1827112 : ExecProjectSet(PlanState *pstate)
      43             : {
      44     1827112 :     ProjectSetState *node = castNode(ProjectSetState, pstate);
      45             :     TupleTableSlot *outerTupleSlot;
      46             :     TupleTableSlot *resultSlot;
      47             :     PlanState  *outerPlan;
      48             :     ExprContext *econtext;
      49             : 
      50     1827112 :     CHECK_FOR_INTERRUPTS();
      51             : 
      52     1827112 :     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     1827112 :     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     1827112 :     if (node->pending_srf_tuples)
      67             :     {
      68     1802640 :         resultSlot = ExecProjectSRF(node, true);
      69             : 
      70     1802640 :         if (resultSlot != NULL)
      71     1722684 :             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      169410 :         MemoryContextReset(node->argcontext);
      89             : 
      90             :         /*
      91             :          * Retrieve tuples from the outer plan until there are no more.
      92             :          */
      93      169410 :         outerPlan = outerPlanState(node);
      94      169410 :         outerTupleSlot = ExecProcNode(outerPlan);
      95             : 
      96      169410 :         if (TupIsNull(outerTupleSlot))
      97       22868 :             return NULL;
      98             : 
      99             :         /*
     100             :          * Prepare to compute projection expressions, which will expect to
     101             :          * access the input tuples as varno OUTER.
     102             :          */
     103      146542 :         econtext->ecxt_outertuple = outerTupleSlot;
     104             : 
     105             :         /* Evaluate the expressions */
     106      146542 :         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      145040 :         if (resultSlot)
     114       80058 :             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       64982 :         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     1949182 : ExecProjectSRF(ProjectSetState *node, bool continuing)
     140             : {
     141     1949182 :     TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
     142     1949182 :     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     1949182 :     ExecClearTuple(resultSlot);
     149             : 
     150             :     /* Call SRFs, as well as plain expressions, in per-tuple context */
     151     1949182 :     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     1949182 :     node->pending_srf_tuples = false;
     158             : 
     159     1949182 :     hassrf = hasresult = false;
     160     5651030 :     for (argno = 0; argno < node->nelems; argno++)
     161             :     {
     162     3703350 :         Node       *elem = node->elems[argno];
     163     3703350 :         ExprDoneCond *isdone = &node->elemdone[argno];
     164     3703350 :         Datum      *result = &resultSlot->tts_values[argno];
     165     3703350 :         bool       *isnull = &resultSlot->tts_isnull[argno];
     166             : 
     167     3703350 :         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       30020 :             *result = (Datum) 0;
     174       30020 :             *isnull = true;
     175       30020 :             hassrf = true;
     176             :         }
     177     3673330 :         else if (IsA(elem, SetExprState))
     178             :         {
     179             :             /*
     180             :              * Evaluate SRF - possibly continuing previously started output.
     181             :              */
     182     2120474 :             *result = ExecMakeFunctionResultSet((SetExprState *) elem,
     183             :                                                 econtext, node->argcontext,
     184             :                                                 isnull, isdone);
     185             : 
     186     2118972 :             if (*isdone != ExprEndResult)
     187     1968224 :                 hasresult = true;
     188     2118972 :             if (*isdone == ExprMultipleResult)
     189     1968218 :                 node->pending_srf_tuples = true;
     190     2118972 :             hassrf = true;
     191             :         }
     192             :         else
     193             :         {
     194             :             /* Non-SRF tlist expression, just evaluate normally. */
     195     1552856 :             *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
     196     1552856 :             *isdone = ExprSingleResult;
     197             :         }
     198             :     }
     199             : 
     200     1947680 :     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     1947680 :     if (hasresult)
     210             :     {
     211     1802742 :         ExecStoreVirtualTuple(resultSlot);
     212     1802742 :         return resultSlot;
     213             :     }
     214             : 
     215      144938 :     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        8904 : 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        8904 :     state = makeNode(ProjectSetState);
     240        8904 :     state->ps.plan = (Plan *) node;
     241        8904 :     state->ps.state = estate;
     242        8904 :     state->ps.ExecProcNode = ExecProjectSet;
     243             : 
     244        8904 :     state->pending_srf_tuples = false;
     245             : 
     246             :     /*
     247             :      * Miscellaneous initialization
     248             :      *
     249             :      * create expression context for node
     250             :      */
     251        8904 :     ExecAssignExprContext(estate, &state->ps);
     252             : 
     253             :     /*
     254             :      * initialize child nodes
     255             :      */
     256        8904 :     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        8904 :     ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual);
     267             : 
     268             :     /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
     269        8904 :     state->nelems = list_length(node->plan.targetlist);
     270        8904 :     state->elems = (Node **)
     271        8904 :         palloc(sizeof(Node *) * state->nelems);
     272        8904 :     state->elemdone = (ExprDoneCond *)
     273        8904 :         palloc(sizeof(ExprDoneCond) * state->nelems);
     274             : 
     275             :     /*
     276             :      * Build expressions to evaluate targetlist.  We can't use
     277             :      * ExecBuildProjectionInfo here, since that doesn't deal with SRFs.
     278             :      * Instead compile each expression separately, using
     279             :      * ExecInitFunctionResultSet where applicable.
     280             :      */
     281        8904 :     off = 0;
     282       19528 :     foreach(lc, node->plan.targetlist)
     283             :     {
     284       10626 :         TargetEntry *te = (TargetEntry *) lfirst(lc);
     285       10626 :         Expr       *expr = te->expr;
     286             : 
     287       10626 :         if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
     288        1570 :             (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
     289             :         {
     290        9060 :             state->elems[off] = (Node *)
     291        9062 :                 ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
     292             :                                           &state->ps);
     293             :         }
     294             :         else
     295             :         {
     296             :             Assert(!expression_returns_set((Node *) expr));
     297        1564 :             state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
     298             :         }
     299             : 
     300       10624 :         off++;
     301             :     }
     302             : 
     303             :     /* We don't support any qual on ProjectSet nodes */
     304             :     Assert(node->plan.qual == NIL);
     305             : 
     306             :     /*
     307             :      * Create a memory context that ExecMakeFunctionResultSet can use to
     308             :      * evaluate function arguments in.  We can't use the per-tuple context for
     309             :      * this because it gets reset too often; but we don't want to leak
     310             :      * evaluation results into the query-lifespan context either.  We use one
     311             :      * context for the arguments of all tSRFs, as they have roughly equivalent
     312             :      * lifetimes.
     313             :      */
     314        8902 :     state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
     315             :                                               "tSRF function arguments",
     316             :                                               ALLOCSET_DEFAULT_SIZES);
     317             : 
     318        8902 :     return state;
     319             : }
     320             : 
     321             : /* ----------------------------------------------------------------
     322             :  *      ExecEndProjectSet
     323             :  *
     324             :  *      frees up storage allocated through C routines
     325             :  * ----------------------------------------------------------------
     326             :  */
     327             : void
     328        7392 : ExecEndProjectSet(ProjectSetState *node)
     329             : {
     330             :     /*
     331             :      * shut down subplans
     332             :      */
     333        7392 :     ExecEndNode(outerPlanState(node));
     334        7392 : }
     335             : 
     336             : void
     337       16350 : ExecReScanProjectSet(ProjectSetState *node)
     338             : {
     339       16350 :     PlanState  *outerPlan = outerPlanState(node);
     340             : 
     341             :     /* Forget any incompletely-evaluated SRFs */
     342       16350 :     node->pending_srf_tuples = false;
     343             : 
     344             :     /*
     345             :      * If chgParam of subnode is not null then plan will be re-scanned by
     346             :      * first ExecProcNode.
     347             :      */
     348       16350 :     if (outerPlan->chgParam == NULL)
     349       16350 :         ExecReScan(outerPlan);
     350       16350 : }

Generated by: LCOV version 1.14