LCOV - differential code coverage report
Current view: top level - src/backend/executor - nodeProjectSet.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: 7a15cff1f11193467898da1c1fabf06fd2caee04 vs 84a3778c79c2d28b4dc281d03ef2ab019b16483b Lines: 100.0 % 87 87 2 85 4
Current Date: 2025-12-15 18:36:29 -0500 Functions: 100.0 % 5 5 1 4
Baseline: lcov-20251216-010103-baseline Branches: 82.7 % 52 43 9 43
Baseline Date: 2025-12-15 13:30:48 -0800 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(1,7] days: 100.0 % 2 2 2
(360..) days: 100.0 % 85 85 85
Function coverage date bins:
(360..) days: 100.0 % 5 5 1 4
Branch coverage date bins:
(360..) days: 82.7 % 52 43 9 43

 Age         Owner                    Branch data    TLA  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-2025, 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 *
 3074 andres@anarazel.de         42                 :CBC     1414405 : ExecProjectSet(PlanState *pstate)
                                 43                 :                : {
                                 44                 :        1414405 :     ProjectSetState *node = castNode(ProjectSetState, pstate);
                                 45                 :                :     TupleTableSlot *outerTupleSlot;
                                 46                 :                :     TupleTableSlot *resultSlot;
                                 47                 :                :     PlanState  *outerPlan;
                                 48                 :                :     ExprContext *econtext;
                                 49                 :                : 
 3066                            50         [ -  + ]:        1414405 :     CHECK_FOR_INTERRUPTS();
                                 51                 :                : 
 3254                            52                 :        1414405 :     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                 :                :      */
 2991                            59                 :        1414405 :     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                 :                :      */
 3254                            66         [ +  + ]:        1414405 :     if (node->pending_srf_tuples)
                                 67                 :                :     {
                                 68                 :        1399430 :         resultSlot = ExecProjectSRF(node, true);
                                 69                 :                : 
                                 70         [ +  + ]:        1399430 :         if (resultSlot != NULL)
                                 71                 :        1352677 :             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                 :                :          */
  780 tgl@sss.pgh.pa.us          88                 :         101061 :         MemoryContextReset(node->argcontext);
                                 89                 :                : 
                                 90                 :                :         /*
                                 91                 :                :          * Retrieve tuples from the outer plan until there are no more.
                                 92                 :                :          */
 3254 andres@anarazel.de         93                 :         101061 :         outerPlan = outerPlanState(node);
                                 94                 :         101061 :         outerTupleSlot = ExecProcNode(outerPlan);
                                 95                 :                : 
                                 96   [ +  +  +  + ]:         101061 :         if (TupIsNull(outerTupleSlot))
                                 97                 :          14166 :             return NULL;
                                 98                 :                : 
                                 99                 :                :         /*
                                100                 :                :          * Prepare to compute projection expressions, which will expect to
                                101                 :                :          * access the input tuples as varno OUTER.
                                102                 :                :          */
                                103                 :          86895 :         econtext->ecxt_outertuple = outerTupleSlot;
                                104                 :                : 
                                105                 :                :         /* Evaluate the expressions */
                                106                 :          86895 :         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         [ +  + ]:          86140 :         if (resultSlot)
                                114                 :          46807 :             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                 :                :          */
  780 tgl@sss.pgh.pa.us         120                 :          39333 :         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 *
 3254 andres@anarazel.de        139                 :        1486325 : ExecProjectSRF(ProjectSetState *node, bool continuing)
                                140                 :                : {
                                141                 :        1486325 :     TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
                                142                 :        1486325 :     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                 :        1486325 :     ExecClearTuple(resultSlot);
                                149                 :                : 
                                150                 :                :     /* Call SRFs, as well as plain expressions, in per-tuple context */
 2993 tgl@sss.pgh.pa.us         151                 :        1486325 :     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                 :                :      */
 3254 andres@anarazel.de        157                 :        1486325 :     node->pending_srf_tuples = false;
                                158                 :                : 
 3199                           159                 :        1486325 :     hassrf = hasresult = false;
                                160         [ +  + ]:        4224610 :     for (argno = 0; argno < node->nelems; argno++)
                                161                 :                :     {
                                162                 :        2739040 :         Node       *elem = node->elems[argno];
 3254                           163                 :        2739040 :         ExprDoneCond *isdone = &node->elemdone[argno];
                                164                 :        2739040 :         Datum      *result = &resultSlot->tts_values[argno];
                                165                 :        2739040 :         bool       *isnull = &resultSlot->tts_isnull[argno];
                                166                 :                : 
                                167   [ +  +  +  + ]:        2739040 :         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                 :          15010 :             *result = (Datum) 0;
                                174                 :          15010 :             *isnull = true;
                                175                 :          15010 :             hassrf = true;
                                176                 :                :         }
 3199                           177         [ +  + ]:        2724030 :         else if (IsA(elem, SetExprState))
                                178                 :                :         {
                                179                 :                :             /*
                                180                 :                :              * Evaluate SRF - possibly continuing previously started output.
                                181                 :                :              */
                                182                 :        1571949 :             *result = ExecMakeFunctionResultSet((SetExprState *) elem,
                                183                 :                :                                                 econtext, node->argcontext,
                                184                 :                :                                                 isnull, isdone);
                                185                 :                : 
 3254                           186         [ +  + ]:        1571194 :             if (*isdone != ExprEndResult)
                                187                 :        1482211 :                 hasresult = true;
                                188         [ +  + ]:        1571194 :             if (*isdone == ExprMultipleResult)
                                189                 :        1482208 :                 node->pending_srf_tuples = true;
                                190                 :        1571194 :             hassrf = true;
                                191                 :                :         }
                                192                 :                :         else
                                193                 :                :         {
                                194                 :                :             /* Non-SRF tlist expression, just evaluate normally. */
 3199                           195                 :        1152081 :             *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
 3254                           196                 :        1152081 :             *isdone = ExprSingleResult;
                                197                 :                :         }
                                198                 :                :     }
                                199                 :                : 
 2993 tgl@sss.pgh.pa.us         200                 :        1485570 :     MemoryContextSwitchTo(oldcontext);
                                201                 :                : 
                                202                 :                :     /* ProjectSet should not be used if there's no SRFs */
 3254 andres@anarazel.de        203         [ -  + ]:        1485570 :     Assert(hassrf);
                                204                 :                : 
                                205                 :                :     /*
                                206                 :                :      * If all the SRFs returned ExprEndResult, we consider that as no row
                                207                 :                :      * being produced.
                                208                 :                :      */
                                209         [ +  + ]:        1485570 :     if (hasresult)
                                210                 :                :     {
                                211                 :        1399484 :         ExecStoreVirtualTuple(resultSlot);
                                212                 :        1399484 :         return resultSlot;
                                213                 :                :     }
                                214                 :                : 
                                215                 :          86086 :     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                 :           7088 : ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
                                228                 :                : {
                                229                 :                :     ProjectSetState *state;
                                230                 :                :     ListCell   *lc;
                                231                 :                :     int         off;
                                232                 :                : 
                                233                 :                :     /* check for unsupported flags */
                                234         [ -  + ]:           7088 :     Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
                                235                 :                : 
                                236                 :                :     /*
                                237                 :                :      * create state structure
                                238                 :                :      */
                                239                 :           7088 :     state = makeNode(ProjectSetState);
                                240                 :           7088 :     state->ps.plan = (Plan *) node;
                                241                 :           7088 :     state->ps.state = estate;
 3074                           242                 :           7088 :     state->ps.ExecProcNode = ExecProjectSet;
                                243                 :                : 
 3254                           244                 :           7088 :     state->pending_srf_tuples = false;
                                245                 :                : 
                                246                 :                :     /*
                                247                 :                :      * Miscellaneous initialization
                                248                 :                :      *
                                249                 :                :      * create expression context for node
                                250                 :                :      */
                                251                 :           7088 :     ExecAssignExprContext(estate, &state->ps);
                                252                 :                : 
                                253                 :                :     /*
                                254                 :                :      * initialize child nodes
                                255                 :                :      */
                                256                 :           7088 :     outerPlanState(state) = ExecInitNode(outerPlan(node), estate, eflags);
                                257                 :                : 
                                258                 :                :     /*
                                259                 :                :      * we don't use inner plan
                                260                 :                :      */
                                261         [ -  + ]:           7088 :     Assert(innerPlan(node) == NULL);
                                262                 :                : 
                                263                 :                :     /*
                                264                 :                :      * tuple table and result type initialization
                                265                 :                :      */
 2588                           266                 :           7088 :     ExecInitResultTupleSlotTL(&state->ps, &TTSOpsVirtual);
                                267                 :                : 
                                268                 :                :     /* Create workspace for per-tlist-entry expr state & SRF-is-done state */
 3254                           269                 :           7088 :     state->nelems = list_length(node->plan.targetlist);
    6 michael@paquier.xyz       270                 :GNC        7088 :     state->elems = palloc_array(Node *, state->nelems);
                                271                 :           7088 :     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                 :                :      */
 3199 andres@anarazel.de        279                 :CBC        7088 :     off = 0;
                                280   [ +  -  +  +  :          15111 :     foreach(lc, node->plan.targetlist)
                                              +  + ]
                                281                 :                :     {
                                282                 :           8024 :         TargetEntry *te = (TargetEntry *) lfirst(lc);
                                283                 :           8024 :         Expr       *expr = te->expr;
                                284                 :                : 
 2040 tgl@sss.pgh.pa.us         285   [ +  +  +  + ]:           8024 :         if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
                                286   [ +  +  +  - ]:            853 :             (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
                                287                 :                :         {
 3199 andres@anarazel.de        288                 :           7173 :             state->elems[off] = (Node *)
                                289                 :           7174 :                 ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
                                290                 :                :                                           &state->ps);
                                291                 :                :         }
                                292                 :                :         else
                                293                 :                :         {
                                294         [ -  + ]:            850 :             Assert(!expression_returns_set((Node *) expr));
                                295                 :            850 :             state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
                                296                 :                :         }
                                297                 :                : 
                                298                 :           8023 :         off++;
                                299                 :                :     }
                                300                 :                : 
                                301                 :                :     /* We don't support any qual on ProjectSet nodes */
 2860                           302         [ -  + ]:           7087 :     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                 :                :      */
 2991                           312                 :           7087 :     state->argcontext = AllocSetContextCreate(CurrentMemoryContext,
                                313                 :                :                                               "tSRF function arguments",
                                314                 :                :                                               ALLOCSET_DEFAULT_SIZES);
                                315                 :                : 
 3254                           316                 :           7087 :     return state;
                                317                 :                : }
                                318                 :                : 
                                319                 :                : /* ----------------------------------------------------------------
                                320                 :                :  *      ExecEndProjectSet
                                321                 :                :  *
                                322                 :                :  *      frees up storage allocated through C routines
                                323                 :                :  * ----------------------------------------------------------------
                                324                 :                :  */
                                325                 :                : void
                                326                 :           6328 : ExecEndProjectSet(ProjectSetState *node)
                                327                 :                : {
                                328                 :                :     /*
                                329                 :                :      * shut down subplans
                                330                 :                :      */
                                331                 :           6328 :     ExecEndNode(outerPlanState(node));
                                332                 :           6328 : }
                                333                 :                : 
                                334                 :                : void
                                335                 :           9101 : ExecReScanProjectSet(ProjectSetState *node)
                                336                 :                : {
 1258 tgl@sss.pgh.pa.us         337                 :           9101 :     PlanState  *outerPlan = outerPlanState(node);
                                338                 :                : 
                                339                 :                :     /* Forget any incompletely-evaluated SRFs */
 3254 andres@anarazel.de        340                 :           9101 :     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                 :                :      */
 1258 tgl@sss.pgh.pa.us         346         [ +  - ]:           9101 :     if (outerPlan->chgParam == NULL)
                                347                 :           9101 :         ExecReScan(outerPlan);
 3254 andres@anarazel.de        348                 :           9101 : }
        

Generated by: LCOV version 2.4-beta