LCOV - code coverage report
Current view: top level - src/backend/executor - nodeCtescan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 74 78 94.9 %
Date: 2024-04-20 00:11:33 Functions: 6 6 100.0 %
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-2024, 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      300094 : 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      300094 :     estate = node->ss.ps.state;
      44      300094 :     dir = estate->es_direction;
      45      300094 :     forward = ScanDirectionIsForward(dir);
      46      300094 :     tuplestorestate = node->leader->cte_table;
      47      300094 :     tuplestore_select_read_pointer(tuplestorestate, node->readptr);
      48      300094 :     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      300094 :     eof_tuplestore = tuplestore_ateof(tuplestorestate);
      55             : 
      56      300094 :     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      300094 :     if (!eof_tuplestore)
      80             :     {
      81      131818 :         if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
      82      123184 :             return slot;
      83        8634 :         if (forward)
      84        8634 :             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      176910 :     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      170402 :         cteslot = ExecProcNode(node->cteplanstate);
     104      170384 :         if (TupIsNull(cteslot))
     105             :         {
     106        1966 :             node->leader->eof_cte = true;
     107        1966 :             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      168418 :         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      168418 :         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      168418 :         return ExecCopySlot(slot, cteslot);
     133             :     }
     134             : 
     135             :     /*
     136             :      * Nothing left ...
     137             :      */
     138        6508 :     return ExecClearTuple(slot);
     139             : }
     140             : 
     141             : /*
     142             :  * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
     143             :  */
     144             : static bool
     145           4 : CteScanRecheck(CteScanState *node, TupleTableSlot *slot)
     146             : {
     147             :     /* nothing to check */
     148           4 :     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      291126 : ExecCteScan(PlanState *pstate)
     161             : {
     162      291126 :     CteScanState *node = castNode(CteScanState, pstate);
     163             : 
     164      291126 :     return ExecScan(&node->ss,
     165             :                     (ExecScanAccessMtd) CteScanNext,
     166             :                     (ExecScanRecheckMtd) CteScanRecheck);
     167             : }
     168             : 
     169             : 
     170             : /* ----------------------------------------------------------------
     171             :  *      ExecInitCteScan
     172             :  * ----------------------------------------------------------------
     173             :  */
     174             : CteScanState *
     175        3212 : 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        3212 :     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        3212 :     scanstate = makeNode(CteScanState);
     207        3212 :     scanstate->ss.ps.plan = (Plan *) node;
     208        3212 :     scanstate->ss.ps.state = estate;
     209        3212 :     scanstate->ss.ps.ExecProcNode = ExecCteScan;
     210        3212 :     scanstate->eflags = eflags;
     211        3212 :     scanstate->cte_table = NULL;
     212        3212 :     scanstate->eof_cte = false;
     213             : 
     214             :     /*
     215             :      * Find the already-initialized plan for the CTE query.
     216             :      */
     217        6424 :     scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
     218        3212 :                                                      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        3212 :     prmdata = &(estate->es_param_exec_vals[node->cteParam]);
     227             :     Assert(prmdata->execPlan == NULL);
     228             :     Assert(!prmdata->isnull);
     229        3212 :     scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
     230        3212 :     if (scanstate->leader == NULL)
     231             :     {
     232             :         /* I am the leader */
     233        1986 :         prmdata->value = PointerGetDatum(scanstate);
     234        1986 :         scanstate->leader = scanstate;
     235        1986 :         scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
     236        1986 :         tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
     237        1986 :         scanstate->readptr = 0;
     238             :     }
     239             :     else
     240             :     {
     241             :         /* Not the leader */
     242             :         /* Create my own read pointer, and ensure it is at start */
     243        1226 :         scanstate->readptr =
     244        1226 :             tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
     245             :                                           scanstate->eflags);
     246        1226 :         tuplestore_select_read_pointer(scanstate->leader->cte_table,
     247             :                                        scanstate->readptr);
     248        1226 :         tuplestore_rescan(scanstate->leader->cte_table);
     249             :     }
     250             : 
     251             :     /*
     252             :      * Miscellaneous initialization
     253             :      *
     254             :      * create expression context for node
     255             :      */
     256        3212 :     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        3212 :     ExecInitScanTupleSlot(estate, &scanstate->ss,
     263             :                           ExecGetResultType(scanstate->cteplanstate),
     264             :                           &TTSOpsMinimalTuple);
     265             : 
     266             :     /*
     267             :      * Initialize result type and projection.
     268             :      */
     269        3212 :     ExecInitResultTypeTL(&scanstate->ss.ps);
     270        3212 :     ExecAssignScanProjectionInfo(&scanstate->ss);
     271             : 
     272             :     /*
     273             :      * initialize child expressions
     274             :      */
     275        3212 :     scanstate->ss.ps.qual =
     276        3212 :         ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
     277             : 
     278        3212 :     return scanstate;
     279             : }
     280             : 
     281             : /* ----------------------------------------------------------------
     282             :  *      ExecEndCteScan
     283             :  *
     284             :  *      frees any storage allocated through C routines.
     285             :  * ----------------------------------------------------------------
     286             :  */
     287             : void
     288        3186 : ExecEndCteScan(CteScanState *node)
     289             : {
     290             :     /*
     291             :      * If I am the leader, free the tuplestore.
     292             :      */
     293        3186 :     if (node->leader == node)
     294             :     {
     295        1962 :         tuplestore_end(node->cte_table);
     296        1962 :         node->cte_table = NULL;
     297             :     }
     298        3186 : }
     299             : 
     300             : /* ----------------------------------------------------------------
     301             :  *      ExecReScanCteScan
     302             :  *
     303             :  *      Rescans the relation.
     304             :  * ----------------------------------------------------------------
     305             :  */
     306             : void
     307        5900 : ExecReScanCteScan(CteScanState *node)
     308             : {
     309        5900 :     Tuplestorestate *tuplestorestate = node->leader->cte_table;
     310             : 
     311        5900 :     if (node->ss.ps.ps_ResultTupleSlot)
     312         194 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     313             : 
     314        5900 :     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        5900 :     if (node->leader->cteplanstate->chgParam != NULL)
     325             :     {
     326         296 :         tuplestore_clear(tuplestorestate);
     327         296 :         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        5604 :         tuplestore_select_read_pointer(tuplestorestate, node->readptr);
     337        5604 :         tuplestore_rescan(tuplestorestate);
     338             :     }
     339        5900 : }

Generated by: LCOV version 1.14