LCOV - code coverage report
Current view: top level - src/backend/executor - nodeCtescan.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 92.4 % 105 97
Test Date: 2026-01-26 10:56:24 Functions: 83.3 % 6 5
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 67.5 % 40 27

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * nodeCtescan.c
       4                 :             :  *        routines to handle CteScan nodes.
       5                 :             :  *
       6                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       7                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
       8                 :             :  *
       9                 :             :  *
      10                 :             :  * IDENTIFICATION
      11                 :             :  *        src/backend/executor/nodeCtescan.c
      12                 :             :  *
      13                 :             :  *-------------------------------------------------------------------------
      14                 :             :  */
      15                 :             : 
      16                 :             : #include "postgres.h"
      17                 :             : 
      18                 :             : #include "executor/executor.h"
      19                 :             : #include "executor/nodeCtescan.h"
      20                 :             : #include "miscadmin.h"
      21                 :             : 
      22                 :             : static TupleTableSlot *CteScanNext(CteScanState *node);
      23                 :             : 
      24                 :             : /* ----------------------------------------------------------------
      25                 :             :  *              CteScanNext
      26                 :             :  *
      27                 :             :  *              This is a workhorse for ExecCteScan
      28                 :             :  * ----------------------------------------------------------------
      29                 :             :  */
      30                 :             : static TupleTableSlot *
      31                 :       25135 : CteScanNext(CteScanState *node)
      32                 :             : {
      33                 :       25135 :         EState     *estate;
      34                 :       25135 :         ScanDirection dir;
      35                 :       25135 :         bool            forward;
      36                 :       25135 :         Tuplestorestate *tuplestorestate;
      37                 :       25135 :         bool            eof_tuplestore;
      38                 :       25135 :         TupleTableSlot *slot;
      39                 :             : 
      40                 :             :         /*
      41                 :             :          * get state info from node
      42                 :             :          */
      43                 :       25135 :         estate = node->ss.ps.state;
      44                 :       25135 :         dir = estate->es_direction;
      45                 :       25135 :         forward = ScanDirectionIsForward(dir);
      46                 :       25135 :         tuplestorestate = node->leader->cte_table;
      47                 :       25135 :         tuplestore_select_read_pointer(tuplestorestate, node->readptr);
      48                 :       25135 :         slot = node->ss.ss_ScanTupleSlot;
      49                 :             : 
      50                 :             :         /*
      51                 :             :          * If we are not at the end of the tuplestore, or are going backwards, try
      52                 :             :          * to fetch a tuple from tuplestore.
      53                 :             :          */
      54                 :       25135 :         eof_tuplestore = tuplestore_ateof(tuplestorestate);
      55                 :             : 
      56   [ -  +  #  # ]:       25135 :         if (!forward && eof_tuplestore)
      57                 :             :         {
      58         [ #  # ]:           0 :                 if (!node->leader->eof_cte)
      59                 :             :                 {
      60                 :             :                         /*
      61                 :             :                          * When reversing direction at tuplestore EOF, the first
      62                 :             :                          * gettupleslot call will fetch the last-added tuple; but we want
      63                 :             :                          * to return the one before that, if possible. So do an extra
      64                 :             :                          * fetch.
      65                 :             :                          */
      66         [ #  # ]:           0 :                         if (!tuplestore_advance(tuplestorestate, forward))
      67                 :           0 :                                 return NULL;    /* the tuplestore must be empty */
      68                 :           0 :                 }
      69                 :           0 :                 eof_tuplestore = false;
      70                 :           0 :         }
      71                 :             : 
      72                 :             :         /*
      73                 :             :          * If we can fetch another tuple from the tuplestore, return it.
      74                 :             :          *
      75                 :             :          * Note: we have to use copy=true in the tuplestore_gettupleslot call,
      76                 :             :          * because we are sharing the tuplestore with other nodes that might write
      77                 :             :          * into the tuplestore before we get called again.
      78                 :             :          */
      79         [ +  + ]:       25135 :         if (!eof_tuplestore)
      80                 :             :         {
      81         [ +  + ]:        5126 :                 if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
      82                 :        4865 :                         return slot;
      83         [ -  + ]:         261 :                 if (forward)
      84                 :         261 :                         eof_tuplestore = true;
      85                 :         261 :         }
      86                 :             : 
      87                 :             :         /*
      88                 :             :          * If necessary, try to fetch another row from the CTE query.
      89                 :             :          *
      90                 :             :          * Note: the eof_cte state variable exists to short-circuit further calls
      91                 :             :          * of the CTE plan.  It's not optional, unfortunately, because some plan
      92                 :             :          * node types are not robust about being called again when they've already
      93                 :             :          * returned NULL.
      94                 :             :          */
      95   [ +  +  +  + ]:       20270 :         if (eof_tuplestore && !node->leader->eof_cte)
      96                 :             :         {
      97                 :       20168 :                 TupleTableSlot *cteslot;
      98                 :             : 
      99                 :             :                 /*
     100                 :             :                  * We can only get here with forward==true, so no need to worry about
     101                 :             :                  * which direction the subplan will go.
     102                 :             :                  */
     103                 :       20168 :                 cteslot = ExecProcNode(node->cteplanstate);
     104   [ +  +  +  + ]:       20168 :                 if (TupIsNull(cteslot))
     105                 :             :                 {
     106                 :         139 :                         node->leader->eof_cte = true;
     107                 :         139 :                         return NULL;
     108                 :             :                 }
     109                 :             : 
     110                 :             :                 /*
     111                 :             :                  * There are corner cases where the subplan could change which
     112                 :             :                  * tuplestore read pointer is active, so be sure to reselect ours
     113                 :             :                  * before storing the tuple we got.
     114                 :             :                  */
     115                 :       20029 :                 tuplestore_select_read_pointer(tuplestorestate, node->readptr);
     116                 :             : 
     117                 :             :                 /*
     118                 :             :                  * Append a copy of the returned tuple to tuplestore.  NOTE: because
     119                 :             :                  * our read pointer is certainly in EOF state, its read position will
     120                 :             :                  * move forward over the added tuple.  This is what we want.  Also,
     121                 :             :                  * any other readers will *not* move past the new tuple, which is what
     122                 :             :                  * they want.
     123                 :             :                  */
     124                 :       20029 :                 tuplestore_puttupleslot(tuplestorestate, cteslot);
     125                 :             : 
     126                 :             :                 /*
     127                 :             :                  * We MUST copy the CTE query's output tuple into our own slot. This
     128                 :             :                  * is because other CteScan nodes might advance the CTE query before
     129                 :             :                  * we are called again, and our output tuple must stay stable over
     130                 :             :                  * that.
     131                 :             :                  */
     132                 :       20029 :                 return ExecCopySlot(slot, cteslot);
     133                 :       20168 :         }
     134                 :             : 
     135                 :             :         /*
     136                 :             :          * Nothing left ...
     137                 :             :          */
     138                 :         102 :         return ExecClearTuple(slot);
     139                 :       25129 : }
     140                 :             : 
     141                 :             : /*
     142                 :             :  * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
     143                 :             :  */
     144                 :             : static bool
     145                 :           0 : CteScanRecheck(CteScanState *node, TupleTableSlot *slot)
     146                 :             : {
     147                 :             :         /* nothing to check */
     148                 :           0 :         return true;
     149                 :             : }
     150                 :             : 
     151                 :             : /* ----------------------------------------------------------------
     152                 :             :  *              ExecCteScan(node)
     153                 :             :  *
     154                 :             :  *              Scans the CTE sequentially and returns the next qualifying tuple.
     155                 :             :  *              We call the ExecScan() routine and pass it the appropriate
     156                 :             :  *              access method functions.
     157                 :             :  * ----------------------------------------------------------------
     158                 :             :  */
     159                 :             : static TupleTableSlot *
     160                 :       15553 : ExecCteScan(PlanState *pstate)
     161                 :             : {
     162                 :       15553 :         CteScanState *node = castNode(CteScanState, pstate);
     163                 :             : 
     164                 :       31106 :         return ExecScan(&node->ss,
     165                 :             :                                         (ExecScanAccessMtd) CteScanNext,
     166                 :             :                                         (ExecScanRecheckMtd) CteScanRecheck);
     167                 :       15553 : }
     168                 :             : 
     169                 :             : 
     170                 :             : /* ----------------------------------------------------------------
     171                 :             :  *              ExecInitCteScan
     172                 :             :  * ----------------------------------------------------------------
     173                 :             :  */
     174                 :             : CteScanState *
     175                 :         212 : ExecInitCteScan(CteScan *node, EState *estate, int eflags)
     176                 :             : {
     177                 :         212 :         CteScanState *scanstate;
     178                 :         212 :         ParamExecData *prmdata;
     179                 :             : 
     180                 :             :         /* check for unsupported flags */
     181         [ +  - ]:         212 :         Assert(!(eflags & EXEC_FLAG_MARK));
     182                 :             : 
     183                 :             :         /*
     184                 :             :          * For the moment we have to force the tuplestore to allow REWIND, because
     185                 :             :          * we might be asked to rescan the CTE even though upper levels didn't
     186                 :             :          * tell us to be prepared to do it efficiently.  Annoying, since this
     187                 :             :          * prevents truncation of the tuplestore.  XXX FIXME
     188                 :             :          *
     189                 :             :          * Note: if we are in an EPQ recheck plan tree, it's likely that no access
     190                 :             :          * to the tuplestore is needed at all, making this even more annoying.
     191                 :             :          * It's not worth improving that as long as all the read pointers would
     192                 :             :          * have REWIND anyway, but if we ever improve this logic then that aspect
     193                 :             :          * should be considered too.
     194                 :             :          */
     195                 :         212 :         eflags |= EXEC_FLAG_REWIND;
     196                 :             : 
     197                 :             :         /*
     198                 :             :          * CteScan should not have any children.
     199                 :             :          */
     200         [ +  - ]:         212 :         Assert(outerPlan(node) == NULL);
     201         [ +  - ]:         212 :         Assert(innerPlan(node) == NULL);
     202                 :             : 
     203                 :             :         /*
     204                 :             :          * create new CteScanState for node
     205                 :             :          */
     206                 :         212 :         scanstate = makeNode(CteScanState);
     207                 :         212 :         scanstate->ss.ps.plan = (Plan *) node;
     208                 :         212 :         scanstate->ss.ps.state = estate;
     209                 :         212 :         scanstate->ss.ps.ExecProcNode = ExecCteScan;
     210                 :         212 :         scanstate->eflags = eflags;
     211                 :         212 :         scanstate->cte_table = NULL;
     212                 :         212 :         scanstate->eof_cte = false;
     213                 :             : 
     214                 :             :         /*
     215                 :             :          * Find the already-initialized plan for the CTE query.
     216                 :             :          */
     217                 :         424 :         scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
     218                 :         212 :                                                                                                          node->ctePlanId - 1);
     219                 :             : 
     220                 :             :         /*
     221                 :             :          * The Param slot associated with the CTE query is used to hold a pointer
     222                 :             :          * to the CteState of the first CteScan node that initializes for this
     223                 :             :          * CTE.  This node will be the one that holds the shared state for all the
     224                 :             :          * CTEs, particularly the shared tuplestore.
     225                 :             :          */
     226                 :         212 :         prmdata = &(estate->es_param_exec_vals[node->cteParam]);
     227         [ +  - ]:         212 :         Assert(prmdata->execPlan == NULL);
     228         [ +  - ]:         212 :         Assert(!prmdata->isnull);
     229                 :         212 :         scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
     230         [ +  + ]:         212 :         if (scanstate->leader == NULL)
     231                 :             :         {
     232                 :             :                 /* I am the leader */
     233                 :         175 :                 prmdata->value = PointerGetDatum(scanstate);
     234                 :         175 :                 scanstate->leader = scanstate;
     235                 :         175 :                 scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
     236                 :         175 :                 tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
     237                 :         175 :                 scanstate->readptr = 0;
     238                 :         175 :         }
     239                 :             :         else
     240                 :             :         {
     241                 :             :                 /* Not the leader */
     242                 :             :                 /* Create my own read pointer, and ensure it is at start */
     243                 :          37 :                 scanstate->readptr =
     244                 :          74 :                         tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
     245                 :          37 :                                                                                   scanstate->eflags);
     246                 :          74 :                 tuplestore_select_read_pointer(scanstate->leader->cte_table,
     247                 :          37 :                                                                            scanstate->readptr);
     248                 :          37 :                 tuplestore_rescan(scanstate->leader->cte_table);
     249                 :             :         }
     250                 :             : 
     251                 :             :         /*
     252                 :             :          * Miscellaneous initialization
     253                 :             :          *
     254                 :             :          * create expression context for node
     255                 :             :          */
     256                 :         212 :         ExecAssignExprContext(estate, &scanstate->ss.ps);
     257                 :             : 
     258                 :             :         /*
     259                 :             :          * The scan tuple type (ie, the rowtype we expect to find in the work
     260                 :             :          * table) is the same as the result rowtype of the CTE query.
     261                 :             :          */
     262                 :         424 :         ExecInitScanTupleSlot(estate, &scanstate->ss,
     263                 :         212 :                                                   ExecGetResultType(scanstate->cteplanstate),
     264                 :             :                                                   &TTSOpsMinimalTuple);
     265                 :             : 
     266                 :             :         /*
     267                 :             :          * Initialize result type and projection.
     268                 :             :          */
     269                 :         212 :         ExecInitResultTypeTL(&scanstate->ss.ps);
     270                 :         212 :         ExecAssignScanProjectionInfo(&scanstate->ss);
     271                 :             : 
     272                 :             :         /*
     273                 :             :          * initialize child expressions
     274                 :             :          */
     275                 :         212 :         scanstate->ss.ps.qual =
     276                 :         212 :                 ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
     277                 :             : 
     278                 :         424 :         return scanstate;
     279                 :         212 : }
     280                 :             : 
     281                 :             : /* ----------------------------------------------------------------
     282                 :             :  *              ExecEndCteScan
     283                 :             :  *
     284                 :             :  *              frees any storage allocated through C routines.
     285                 :             :  * ----------------------------------------------------------------
     286                 :             :  */
     287                 :             : void
     288                 :         209 : ExecEndCteScan(CteScanState *node)
     289                 :             : {
     290                 :             :         /*
     291                 :             :          * If I am the leader, free the tuplestore.
     292                 :             :          */
     293         [ +  + ]:         209 :         if (node->leader == node)
     294                 :             :         {
     295                 :         172 :                 tuplestore_end(node->cte_table);
     296                 :         172 :                 node->cte_table = NULL;
     297                 :         172 :         }
     298                 :         209 : }
     299                 :             : 
     300                 :             : /* ----------------------------------------------------------------
     301                 :             :  *              ExecReScanCteScan
     302                 :             :  *
     303                 :             :  *              Rescans the relation.
     304                 :             :  * ----------------------------------------------------------------
     305                 :             :  */
     306                 :             : void
     307                 :         105 : ExecReScanCteScan(CteScanState *node)
     308                 :             : {
     309                 :         105 :         Tuplestorestate *tuplestorestate = node->leader->cte_table;
     310                 :             : 
     311         [ +  + ]:         105 :         if (node->ss.ps.ps_ResultTupleSlot)
     312                 :          17 :                 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     313                 :             : 
     314                 :         105 :         ExecScanReScan(&node->ss);
     315                 :             : 
     316                 :             :         /*
     317                 :             :          * Clear the tuplestore if a new scan of the underlying CTE is required.
     318                 :             :          * This implicitly resets all the tuplestore's read pointers.  Note that
     319                 :             :          * multiple CTE nodes might redundantly clear the tuplestore; that's OK,
     320                 :             :          * and not unduly expensive.  We'll stop taking this path as soon as
     321                 :             :          * somebody has attempted to read something from the underlying CTE
     322                 :             :          * (thereby causing its chgParam to be cleared).
     323                 :             :          */
     324         [ +  + ]:         105 :         if (node->leader->cteplanstate->chgParam != NULL)
     325                 :             :         {
     326                 :          16 :                 tuplestore_clear(tuplestorestate);
     327                 :          16 :                 node->leader->eof_cte = false;
     328                 :          16 :         }
     329                 :             :         else
     330                 :             :         {
     331                 :             :                 /*
     332                 :             :                  * Else, just rewind my own pointer.  Either the underlying CTE
     333                 :             :                  * doesn't need a rescan (and we can re-read what's in the tuplestore
     334                 :             :                  * now), or somebody else already took care of it.
     335                 :             :                  */
     336                 :          89 :                 tuplestore_select_read_pointer(tuplestorestate, node->readptr);
     337                 :          89 :                 tuplestore_rescan(tuplestorestate);
     338                 :             :         }
     339                 :         105 : }
        

Generated by: LCOV version 2.3.2-1