LCOV - code coverage report
Current view: top level - src/backend/executor - nodeCtescan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 94.9 % 78 74
Test Date: 2026-02-28 23:15:01 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit

            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       250040 : CteScanNext(CteScanState *node)
      32              : {
      33              :     EState     *estate;
      34              :     ScanDirection dir;
      35              :     bool        forward;
      36              :     Tuplestorestate *tuplestorestate;
      37              :     bool        eof_tuplestore;
      38              :     TupleTableSlot *slot;
      39              : 
      40              :     /*
      41              :      * get state info from node
      42              :      */
      43       250040 :     estate = node->ss.ps.state;
      44       250040 :     dir = estate->es_direction;
      45       250040 :     forward = ScanDirectionIsForward(dir);
      46       250040 :     tuplestorestate = node->leader->cte_table;
      47       250040 :     tuplestore_select_read_pointer(tuplestorestate, node->readptr);
      48       250040 :     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       250040 :     eof_tuplestore = tuplestore_ateof(tuplestorestate);
      55              : 
      56       250040 :     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              :         }
      69            0 :         eof_tuplestore = false;
      70              :     }
      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       250040 :     if (!eof_tuplestore)
      80              :     {
      81       109482 :         if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
      82       104278 :             return slot;
      83         5204 :         if (forward)
      84         5204 :             eof_tuplestore = true;
      85              :     }
      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       145762 :     if (eof_tuplestore && !node->leader->eof_cte)
      96              :     {
      97              :         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       142059 :         cteslot = ExecProcNode(node->cteplanstate);
     104       142050 :         if (TupIsNull(cteslot))
     105              :         {
     106         1418 :             node->leader->eof_cte = true;
     107         1418 :             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       140632 :         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       140632 :         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       140632 :         return ExecCopySlot(slot, cteslot);
     133              :     }
     134              : 
     135              :     /*
     136              :      * Nothing left ...
     137              :      */
     138         3703 :     return ExecClearTuple(slot);
     139              : }
     140              : 
     141              : /*
     142              :  * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
     143              :  */
     144              : static bool
     145            2 : CteScanRecheck(CteScanState *node, TupleTableSlot *slot)
     146              : {
     147              :     /* nothing to check */
     148            2 :     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       218458 : ExecCteScan(PlanState *pstate)
     161              : {
     162       218458 :     CteScanState *node = castNode(CteScanState, pstate);
     163              : 
     164       218458 :     return ExecScan(&node->ss,
     165              :                     (ExecScanAccessMtd) CteScanNext,
     166              :                     (ExecScanRecheckMtd) CteScanRecheck);
     167              : }
     168              : 
     169              : 
     170              : /* ----------------------------------------------------------------
     171              :  *      ExecInitCteScan
     172              :  * ----------------------------------------------------------------
     173              :  */
     174              : CteScanState *
     175         2291 : ExecInitCteScan(CteScan *node, EState *estate, int eflags)
     176              : {
     177              :     CteScanState *scanstate;
     178              :     ParamExecData *prmdata;
     179              : 
     180              :     /* check for unsupported flags */
     181              :     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         2291 :     eflags |= EXEC_FLAG_REWIND;
     196              : 
     197              :     /*
     198              :      * CteScan should not have any children.
     199              :      */
     200              :     Assert(outerPlan(node) == NULL);
     201              :     Assert(innerPlan(node) == NULL);
     202              : 
     203              :     /*
     204              :      * create new CteScanState for node
     205              :      */
     206         2291 :     scanstate = makeNode(CteScanState);
     207         2291 :     scanstate->ss.ps.plan = (Plan *) node;
     208         2291 :     scanstate->ss.ps.state = estate;
     209         2291 :     scanstate->ss.ps.ExecProcNode = ExecCteScan;
     210         2291 :     scanstate->eflags = eflags;
     211         2291 :     scanstate->cte_table = NULL;
     212         2291 :     scanstate->eof_cte = false;
     213              : 
     214              :     /*
     215              :      * Find the already-initialized plan for the CTE query.
     216              :      */
     217         4582 :     scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
     218         2291 :                                                      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         2291 :     prmdata = &(estate->es_param_exec_vals[node->cteParam]);
     227              :     Assert(prmdata->execPlan == NULL);
     228              :     Assert(!prmdata->isnull);
     229         2291 :     scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
     230         2291 :     if (scanstate->leader == NULL)
     231              :     {
     232              :         /* I am the leader */
     233         1357 :         prmdata->value = PointerGetDatum(scanstate);
     234         1357 :         scanstate->leader = scanstate;
     235         1357 :         scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
     236         1357 :         tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
     237         1357 :         scanstate->readptr = 0;
     238              :     }
     239              :     else
     240              :     {
     241              :         /* Not the leader */
     242              :         /* Create my own read pointer, and ensure it is at start */
     243          934 :         scanstate->readptr =
     244          934 :             tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
     245              :                                           scanstate->eflags);
     246          934 :         tuplestore_select_read_pointer(scanstate->leader->cte_table,
     247              :                                        scanstate->readptr);
     248          934 :         tuplestore_rescan(scanstate->leader->cte_table);
     249              :     }
     250              : 
     251              :     /*
     252              :      * Miscellaneous initialization
     253              :      *
     254              :      * create expression context for node
     255              :      */
     256         2291 :     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         2291 :     ExecInitScanTupleSlot(estate, &scanstate->ss,
     263              :                           ExecGetResultType(scanstate->cteplanstate),
     264              :                           &TTSOpsMinimalTuple);
     265              : 
     266              :     /*
     267              :      * Initialize result type and projection.
     268              :      */
     269         2291 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     270         2291 :     ExecAssignScanProjectionInfo(&scanstate->ss);
     271              : 
     272              :     /*
     273              :      * initialize child expressions
     274              :      */
     275         2291 :     scanstate->ss.ps.qual =
     276         2291 :         ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
     277              : 
     278         2291 :     return scanstate;
     279              : }
     280              : 
     281              : /* ----------------------------------------------------------------
     282              :  *      ExecEndCteScan
     283              :  *
     284              :  *      frees any storage allocated through C routines.
     285              :  * ----------------------------------------------------------------
     286              :  */
     287              : void
     288         2278 : ExecEndCteScan(CteScanState *node)
     289              : {
     290              :     /*
     291              :      * If I am the leader, free the tuplestore.
     292              :      */
     293         2278 :     if (node->leader == node)
     294              :     {
     295         1345 :         tuplestore_end(node->cte_table);
     296         1345 :         node->cte_table = NULL;
     297              :     }
     298         2278 : }
     299              : 
     300              : /* ----------------------------------------------------------------
     301              :  *      ExecReScanCteScan
     302              :  *
     303              :  *      Rescans the relation.
     304              :  * ----------------------------------------------------------------
     305              :  */
     306              : void
     307         3211 : ExecReScanCteScan(CteScanState *node)
     308              : {
     309         3211 :     Tuplestorestate *tuplestorestate = node->leader->cte_table;
     310              : 
     311         3211 :     if (node->ss.ps.ps_ResultTupleSlot)
     312          104 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     313              : 
     314         3211 :     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         3211 :     if (node->leader->cteplanstate->chgParam != NULL)
     325              :     {
     326          278 :         tuplestore_clear(tuplestorestate);
     327          278 :         node->leader->eof_cte = false;
     328              :     }
     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         2933 :         tuplestore_select_read_pointer(tuplestorestate, node->readptr);
     337         2933 :         tuplestore_rescan(tuplestorestate);
     338              :     }
     339         3211 : }
        

Generated by: LCOV version 2.0-1