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

Generated by: LCOV version 1.14