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

Generated by: LCOV version 2.0-1