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

Generated by: LCOV version 1.14