LCOV - code coverage report
Current view: top level - src/backend/executor - nodeProjectSet.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 100.0 % 111 111
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 5 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 82.0 % 50 41

             Branch data     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                 :      178969 : ExecProjectSet(PlanState *pstate)
      43                 :             : {
      44                 :      178969 :         ProjectSetState *node = castNode(ProjectSetState, pstate);
      45                 :      178969 :         TupleTableSlot *outerTupleSlot;
      46                 :      178969 :         TupleTableSlot *resultSlot;
      47                 :      178969 :         PlanState  *outerPlan;
      48                 :      178969 :         ExprContext *econtext;
      49                 :             : 
      50         [ +  - ]:      178969 :         CHECK_FOR_INTERRUPTS();
      51                 :             : 
      52                 :      178969 :         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                 :      178969 :         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         [ +  + ]:      178969 :         if (node->pending_srf_tuples)
      67                 :             :         {
      68                 :      173398 :                 resultSlot = ExecProjectSRF(node, true);
      69                 :             : 
      70         [ +  + ]:      173398 :                 if (resultSlot != NULL)
      71                 :      150291 :                         return resultSlot;
      72                 :       23107 :         }
      73                 :             : 
      74                 :             :         /*
      75                 :             :          * Get another input tuple and project SRFs from it.
      76                 :             :          */
      77                 :       44593 :         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                 :       44593 :                 MemoryContextReset(node->argcontext);
      89                 :             : 
      90                 :             :                 /*
      91                 :             :                  * Retrieve tuples from the outer plan until there are no more.
      92                 :             :                  */
      93                 :       44593 :                 outerPlan = outerPlanState(node);
      94                 :       44593 :                 outerTupleSlot = ExecProcNode(outerPlan);
      95                 :             : 
      96   [ +  +  +  + ]:       44593 :                 if (TupIsNull(outerTupleSlot))
      97                 :        5552 :                         return NULL;
      98                 :             : 
      99                 :             :                 /*
     100                 :             :                  * Prepare to compute projection expressions, which will expect to
     101                 :             :                  * access the input tuples as varno OUTER.
     102                 :             :                  */
     103                 :       39041 :                 econtext->ecxt_outertuple = outerTupleSlot;
     104                 :             : 
     105                 :             :                 /* Evaluate the expressions */
     106                 :       39041 :                 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         [ +  + ]:       39041 :                 if (resultSlot)
     114                 :       23126 :                         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                 :       15915 :                 ResetExprContext(econtext);
     121                 :             :         }
     122                 :             : 
     123                 :             :         return NULL;
     124                 :      178969 : }
     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                 :      212439 : ExecProjectSRF(ProjectSetState *node, bool continuing)
     140                 :             : {
     141                 :      212439 :         TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
     142                 :      212439 :         ExprContext *econtext = node->ps.ps_ExprContext;
     143                 :      212439 :         MemoryContext oldcontext;
     144                 :      212439 :         bool            hassrf PG_USED_FOR_ASSERTS_ONLY;
     145                 :      212439 :         bool            hasresult;
     146                 :      212439 :         int                     argno;
     147                 :             : 
     148                 :      212439 :         ExecClearTuple(resultSlot);
     149                 :             : 
     150                 :             :         /* Call SRFs, as well as plain expressions, in per-tuple context */
     151                 :      212439 :         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                 :      212439 :         node->pending_srf_tuples = false;
     158                 :             : 
     159                 :      212439 :         hassrf = hasresult = false;
     160         [ +  + ]:      629060 :         for (argno = 0; argno < node->nelems; argno++)
     161                 :             :         {
     162                 :      416621 :                 Node       *elem = node->elems[argno];
     163                 :      416621 :                 ExprDoneCond *isdone = &node->elemdone[argno];
     164                 :      416621 :                 Datum      *result = &resultSlot->tts_values[argno];
     165                 :      416621 :                 bool       *isnull = &resultSlot->tts_isnull[argno];
     166                 :             : 
     167   [ +  +  +  + ]:      416621 :                 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                 :        5005 :                         *result = (Datum) 0;
     174                 :        5005 :                         *isnull = true;
     175                 :        5005 :                         hassrf = true;
     176                 :        5005 :                 }
     177         [ +  + ]:      411616 :                 else if (IsA(elem, SetExprState))
     178                 :             :                 {
     179                 :             :                         /*
     180                 :             :                          * Evaluate SRF - possibly continuing previously started output.
     181                 :             :                          */
     182                 :      479956 :                         *result = ExecMakeFunctionResultSet((SetExprState *) elem,
     183                 :      239978 :                                                                                                 econtext, node->argcontext,
     184                 :      239978 :                                                                                                 isnull, isdone);
     185                 :             : 
     186         [ +  + ]:      239978 :                         if (*isdone != ExprEndResult)
     187                 :      199990 :                                 hasresult = true;
     188         [ +  + ]:      239978 :                         if (*isdone == ExprMultipleResult)
     189                 :      199990 :                                 node->pending_srf_tuples = true;
     190                 :      239978 :                         hassrf = true;
     191                 :      239978 :                 }
     192                 :             :                 else
     193                 :             :                 {
     194                 :             :                         /* Non-SRF tlist expression, just evaluate normally. */
     195                 :      171638 :                         *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
     196                 :      171638 :                         *isdone = ExprSingleResult;
     197                 :             :                 }
     198                 :      416621 :         }
     199                 :             : 
     200                 :      212439 :         MemoryContextSwitchTo(oldcontext);
     201                 :             : 
     202                 :             :         /* ProjectSet should not be used if there's no SRFs */
     203         [ +  - ]:      212439 :         Assert(hassrf);
     204                 :             : 
     205                 :             :         /*
     206                 :             :          * If all the SRFs returned ExprEndResult, we consider that as no row
     207                 :             :          * being produced.
     208                 :             :          */
     209         [ +  + ]:      212439 :         if (hasresult)
     210                 :             :         {
     211                 :      173417 :                 ExecStoreVirtualTuple(resultSlot);
     212                 :      173417 :                 return resultSlot;
     213                 :             :         }
     214                 :             : 
     215                 :       39022 :         return NULL;
     216                 :      212439 : }
     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                 :        2001 : ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
     228                 :             : {
     229                 :        2001 :         ProjectSetState *state;
     230                 :        2001 :         ListCell   *lc;
     231                 :        2001 :         int                     off;
     232                 :             : 
     233                 :             :         /* check for unsupported flags */
     234         [ +  - ]:        2001 :         Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
     235                 :             : 
     236                 :             :         /*
     237                 :             :          * create state structure
     238                 :             :          */
     239                 :        2001 :         state = makeNode(ProjectSetState);
     240                 :        2001 :         state->ps.plan = (Plan *) node;
     241                 :        2001 :         state->ps.state = estate;
     242                 :        2001 :         state->ps.ExecProcNode = ExecProjectSet;
     243                 :             : 
     244                 :        2001 :         state->pending_srf_tuples = false;
     245                 :             : 
     246                 :             :         /*
     247                 :             :          * Miscellaneous initialization
     248                 :             :          *
     249                 :             :          * create expression context for node
     250                 :             :          */
     251                 :        2001 :         ExecAssignExprContext(estate, &state->ps);
     252                 :             : 
     253                 :             :         /*
     254                 :             :          * initialize child nodes
     255                 :             :          */
     256                 :        2001 :         outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
     257                 :             : 
     258                 :             :         /*
     259                 :             :          * we don't use inner plan
     260                 :             :          */
     261         [ +  - ]:        2001 :         Assert(innerPlan(node) == NULL);
     262                 :             : 
     263                 :             :         /*
     264                 :             :          * tuple table and result type initialization
     265                 :             :          */
     266                 :        2001 :         ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual);
     267                 :             : 
     268                 :             :         /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
     269                 :        2001 :         state->nelems = list_length(node->plan.targetlist);
     270                 :        2001 :         state->elems = palloc_array(Node *, state->nelems);
     271                 :        2001 :         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                 :        2001 :         off = 0;
     280   [ +  -  +  +  :        4233 :         foreach(lc, node->plan.targetlist)
                   +  + ]
     281                 :             :         {
     282                 :        2232 :                 TargetEntry *te = (TargetEntry *) lfirst(lc);
     283                 :        2232 :                 Expr       *expr = te->expr;
     284                 :             : 
     285   [ +  +  +  - ]:        2233 :                 if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
     286         [ +  + ]:         203 :                         (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
     287                 :             :                 {
     288                 :        2030 :                         state->elems[off] = (Node *)
     289                 :        4060 :                                 ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
     290                 :        2030 :                                                                                   &state->ps);
     291                 :        2030 :                 }
     292                 :             :                 else
     293                 :             :                 {
     294         [ +  - ]:         202 :                         Assert(!expression_returns_set((Node *) expr));
     295                 :         202 :                         state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
     296                 :             :                 }
     297                 :             : 
     298                 :        2232 :                 off++;
     299                 :        2232 :         }
     300                 :             : 
     301                 :             :         /* We don't support any qual on ProjectSet nodes */
     302         [ +  - ]:        2001 :         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                 :        2001 :         state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
     313                 :             :                                                                                           "tSRF function arguments",
     314                 :             :                                                                                           ALLOCSET_DEFAULT_SIZES);
     315                 :             : 
     316                 :        4002 :         return state;
     317                 :        2001 : }
     318                 :             : 
     319                 :             : /* ----------------------------------------------------------------
     320                 :             :  *              ExecEndProjectSet
     321                 :             :  *
     322                 :             :  *              frees up storage allocated through C routines
     323                 :             :  * ----------------------------------------------------------------
     324                 :             :  */
     325                 :             : void
     326                 :        1762 : ExecEndProjectSet(ProjectSetState *node)
     327                 :             : {
     328                 :             :         /*
     329                 :             :          * shut down subplans
     330                 :             :          */
     331                 :        1762 :         ExecEndNode(outerPlanState(node));
     332                 :        1762 : }
     333                 :             : 
     334                 :             : void
     335                 :        4360 : ExecReScanProjectSet(ProjectSetState *node)
     336                 :             : {
     337                 :        4360 :         PlanState  *outerPlan = outerPlanState(node);
     338                 :             : 
     339                 :             :         /* Forget any incompletely-evaluated SRFs */
     340                 :        4360 :         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         [ -  + ]:        4360 :         if (outerPlan->chgParam == NULL)
     347                 :        4360 :                 ExecReScan(outerPlan);
     348                 :        4360 : }
        

Generated by: LCOV version 2.3.2-1