LCOV - code coverage report
Current view: top level - src/backend/executor - nodeTidrangescan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 110 116 94.8 %
Date: 2025-10-10 11:17:28 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nodeTidrangescan.c
       4             :  *    Routines to support TID range scans of relations
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/executor/nodeTidrangescan.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : #include "postgres.h"
      16             : 
      17             : #include "access/relscan.h"
      18             : #include "access/sysattr.h"
      19             : #include "access/tableam.h"
      20             : #include "catalog/pg_operator.h"
      21             : #include "executor/executor.h"
      22             : #include "executor/nodeTidrangescan.h"
      23             : #include "nodes/nodeFuncs.h"
      24             : #include "utils/rel.h"
      25             : 
      26             : 
      27             : /*
      28             :  * It's sufficient to check varattno to identify the CTID variable, as any
      29             :  * Var in the relation scan qual must be for our table.  (Even if it's a
      30             :  * parameterized scan referencing some other table's CTID, the other table's
      31             :  * Var would have become a Param by the time it gets here.)
      32             :  */
      33             : #define IsCTIDVar(node)  \
      34             :     ((node) != NULL && \
      35             :      IsA((node), Var) && \
      36             :      ((Var *) (node))->varattno == SelfItemPointerAttributeNumber)
      37             : 
      38             : typedef enum
      39             : {
      40             :     TIDEXPR_UPPER_BOUND,
      41             :     TIDEXPR_LOWER_BOUND,
      42             : } TidExprType;
      43             : 
      44             : /* Upper or lower range bound for scan */
      45             : typedef struct TidOpExpr
      46             : {
      47             :     TidExprType exprtype;       /* type of op; lower or upper */
      48             :     ExprState  *exprstate;      /* ExprState for a TID-yielding subexpr */
      49             :     bool        inclusive;      /* whether op is inclusive */
      50             : } TidOpExpr;
      51             : 
      52             : /*
      53             :  * For the given 'expr', build and return an appropriate TidOpExpr taking into
      54             :  * account the expr's operator and operand order.
      55             :  */
      56             : static TidOpExpr *
      57        1982 : MakeTidOpExpr(OpExpr *expr, TidRangeScanState *tidstate)
      58             : {
      59        1982 :     Node       *arg1 = get_leftop((Expr *) expr);
      60        1982 :     Node       *arg2 = get_rightop((Expr *) expr);
      61        1982 :     ExprState  *exprstate = NULL;
      62        1982 :     bool        invert = false;
      63             :     TidOpExpr  *tidopexpr;
      64             : 
      65        1982 :     if (IsCTIDVar(arg1))
      66        1946 :         exprstate = ExecInitExpr((Expr *) arg2, &tidstate->ss.ps);
      67          36 :     else if (IsCTIDVar(arg2))
      68             :     {
      69          36 :         exprstate = ExecInitExpr((Expr *) arg1, &tidstate->ss.ps);
      70          36 :         invert = true;
      71             :     }
      72             :     else
      73           0 :         elog(ERROR, "could not identify CTID variable");
      74             : 
      75        1982 :     tidopexpr = (TidOpExpr *) palloc(sizeof(TidOpExpr));
      76        1982 :     tidopexpr->inclusive = false;    /* for now */
      77             : 
      78        1982 :     switch (expr->opno)
      79             :     {
      80          30 :         case TIDLessEqOperator:
      81          30 :             tidopexpr->inclusive = true;
      82             :             /* fall through */
      83         124 :         case TIDLessOperator:
      84         124 :             tidopexpr->exprtype = invert ? TIDEXPR_LOWER_BOUND : TIDEXPR_UPPER_BOUND;
      85         124 :             break;
      86        1794 :         case TIDGreaterEqOperator:
      87        1794 :             tidopexpr->inclusive = true;
      88             :             /* fall through */
      89        1858 :         case TIDGreaterOperator:
      90        1858 :             tidopexpr->exprtype = invert ? TIDEXPR_UPPER_BOUND : TIDEXPR_LOWER_BOUND;
      91        1858 :             break;
      92           0 :         default:
      93           0 :             elog(ERROR, "could not identify CTID operator");
      94             :     }
      95             : 
      96        1982 :     tidopexpr->exprstate = exprstate;
      97             : 
      98        1982 :     return tidopexpr;
      99             : }
     100             : 
     101             : /*
     102             :  * Extract the qual subexpressions that yield TIDs to search for,
     103             :  * and compile them into ExprStates if they're ordinary expressions.
     104             :  */
     105             : static void
     106        1946 : TidExprListCreate(TidRangeScanState *tidrangestate)
     107             : {
     108        1946 :     TidRangeScan *node = (TidRangeScan *) tidrangestate->ss.ps.plan;
     109        1946 :     List       *tidexprs = NIL;
     110             :     ListCell   *l;
     111             : 
     112        3928 :     foreach(l, node->tidrangequals)
     113             :     {
     114        1982 :         OpExpr     *opexpr = lfirst(l);
     115             :         TidOpExpr  *tidopexpr;
     116             : 
     117        1982 :         if (!IsA(opexpr, OpExpr))
     118           0 :             elog(ERROR, "could not identify CTID expression");
     119             : 
     120        1982 :         tidopexpr = MakeTidOpExpr(opexpr, tidrangestate);
     121        1982 :         tidexprs = lappend(tidexprs, tidopexpr);
     122             :     }
     123             : 
     124        1946 :     tidrangestate->trss_tidexprs = tidexprs;
     125        1946 : }
     126             : 
     127             : /* ----------------------------------------------------------------
     128             :  *      TidRangeEval
     129             :  *
     130             :  *      Compute and set node's block and offset range to scan by evaluating
     131             :  *      node->trss_tidexprs.  Returns false if we detect the range cannot
     132             :  *      contain any tuples.  Returns true if it's possible for the range to
     133             :  *      contain tuples.  We don't bother validating that trss_mintid is less
     134             :  *      than or equal to trss_maxtid, as the scan_set_tidrange() table AM
     135             :  *      function will handle that.
     136             :  * ----------------------------------------------------------------
     137             :  */
     138             : static bool
     139        1928 : TidRangeEval(TidRangeScanState *node)
     140             : {
     141        1928 :     ExprContext *econtext = node->ss.ps.ps_ExprContext;
     142             :     ItemPointerData lowerBound;
     143             :     ItemPointerData upperBound;
     144             :     ListCell   *l;
     145             : 
     146             :     /*
     147             :      * Set the upper and lower bounds to the absolute limits of the range of
     148             :      * the ItemPointer type.  Below we'll try to narrow this range on either
     149             :      * side by looking at the TidOpExprs.
     150             :      */
     151        1928 :     ItemPointerSet(&lowerBound, 0, 0);
     152        1928 :     ItemPointerSet(&upperBound, InvalidBlockNumber, PG_UINT16_MAX);
     153             : 
     154        3874 :     foreach(l, node->trss_tidexprs)
     155             :     {
     156        1952 :         TidOpExpr  *tidopexpr = (TidOpExpr *) lfirst(l);
     157             :         ItemPointer itemptr;
     158             :         bool        isNull;
     159             : 
     160             :         /* Evaluate this bound. */
     161             :         itemptr = (ItemPointer)
     162        1952 :             DatumGetPointer(ExecEvalExprSwitchContext(tidopexpr->exprstate,
     163             :                                                       econtext,
     164             :                                                       &isNull));
     165             : 
     166             :         /* If the bound is NULL, *nothing* matches the qual. */
     167        1952 :         if (isNull)
     168           6 :             return false;
     169             : 
     170        1946 :         if (tidopexpr->exprtype == TIDEXPR_LOWER_BOUND)
     171             :         {
     172             :             ItemPointerData lb;
     173             : 
     174        1798 :             ItemPointerCopy(itemptr, &lb);
     175             : 
     176             :             /*
     177             :              * Normalize non-inclusive ranges to become inclusive.  The
     178             :              * resulting ItemPointer here may not be a valid item pointer.
     179             :              */
     180        1798 :             if (!tidopexpr->inclusive)
     181          46 :                 ItemPointerInc(&lb);
     182             : 
     183             :             /* Check if we can narrow the range using this qual */
     184        1798 :             if (ItemPointerCompare(&lb, &lowerBound) > 0)
     185        1798 :                 ItemPointerCopy(&lb, &lowerBound);
     186             :         }
     187             : 
     188         148 :         else if (tidopexpr->exprtype == TIDEXPR_UPPER_BOUND)
     189             :         {
     190             :             ItemPointerData ub;
     191             : 
     192         148 :             ItemPointerCopy(itemptr, &ub);
     193             : 
     194             :             /*
     195             :              * Normalize non-inclusive ranges to become inclusive.  The
     196             :              * resulting ItemPointer here may not be a valid item pointer.
     197             :              */
     198         148 :             if (!tidopexpr->inclusive)
     199          64 :                 ItemPointerDec(&ub);
     200             : 
     201             :             /* Check if we can narrow the range using this qual */
     202         148 :             if (ItemPointerCompare(&ub, &upperBound) < 0)
     203         148 :                 ItemPointerCopy(&ub, &upperBound);
     204             :         }
     205             :     }
     206             : 
     207        1922 :     ItemPointerCopy(&lowerBound, &node->trss_mintid);
     208        1922 :     ItemPointerCopy(&upperBound, &node->trss_maxtid);
     209             : 
     210        1922 :     return true;
     211             : }
     212             : 
     213             : /* ----------------------------------------------------------------
     214             :  *      TidRangeNext
     215             :  *
     216             :  *      Retrieve a tuple from the TidRangeScan node's currentRelation
     217             :  *      using the TIDs in the TidRangeScanState information.
     218             :  *
     219             :  * ----------------------------------------------------------------
     220             :  */
     221             : static TupleTableSlot *
     222        8608 : TidRangeNext(TidRangeScanState *node)
     223             : {
     224             :     TableScanDesc scandesc;
     225             :     EState     *estate;
     226             :     ScanDirection direction;
     227             :     TupleTableSlot *slot;
     228             : 
     229             :     /*
     230             :      * extract necessary information from TID scan node
     231             :      */
     232        8608 :     scandesc = node->ss.ss_currentScanDesc;
     233        8608 :     estate = node->ss.ps.state;
     234        8608 :     slot = node->ss.ss_ScanTupleSlot;
     235        8608 :     direction = estate->es_direction;
     236             : 
     237        8608 :     if (!node->trss_inScan)
     238             :     {
     239             :         /* First time through, compute TID range to scan */
     240        1926 :         if (!TidRangeEval(node))
     241           6 :             return NULL;
     242             : 
     243        1920 :         if (scandesc == NULL)
     244             :         {
     245        1854 :             scandesc = table_beginscan_tidrange(node->ss.ss_currentRelation,
     246             :                                                 estate->es_snapshot,
     247             :                                                 &node->trss_mintid,
     248             :                                                 &node->trss_maxtid);
     249        1854 :             node->ss.ss_currentScanDesc = scandesc;
     250             :         }
     251             :         else
     252             :         {
     253             :             /* rescan with the updated TID range */
     254          66 :             table_rescan_tidrange(scandesc, &node->trss_mintid,
     255             :                                   &node->trss_maxtid);
     256             :         }
     257             : 
     258        1920 :         node->trss_inScan = true;
     259             :     }
     260             : 
     261             :     /* Fetch the next tuple. */
     262        8602 :     if (!table_scan_getnextslot_tidrange(scandesc, direction, slot))
     263             :     {
     264         170 :         node->trss_inScan = false;
     265         170 :         ExecClearTuple(slot);
     266             :     }
     267             : 
     268        8586 :     return slot;
     269             : }
     270             : 
     271             : /*
     272             :  * TidRangeRecheck -- access method routine to recheck a tuple in EvalPlanQual
     273             :  */
     274             : static bool
     275           2 : TidRangeRecheck(TidRangeScanState *node, TupleTableSlot *slot)
     276             : {
     277           2 :     if (!TidRangeEval(node))
     278           0 :         return false;
     279             : 
     280             :     Assert(ItemPointerIsValid(&slot->tts_tid));
     281             : 
     282             :     /* Recheck the ctid is still within range */
     283           4 :     if (ItemPointerCompare(&slot->tts_tid, &node->trss_mintid) < 0 ||
     284           2 :         ItemPointerCompare(&slot->tts_tid, &node->trss_maxtid) > 0)
     285           2 :         return false;
     286             : 
     287           0 :     return true;
     288             : }
     289             : 
     290             : /* ----------------------------------------------------------------
     291             :  *      ExecTidRangeScan(node)
     292             :  *
     293             :  *      Scans the relation using tids and returns the next qualifying tuple.
     294             :  *      We call the ExecScan() routine and pass it the appropriate
     295             :  *      access method functions.
     296             :  *
     297             :  *      Conditions:
     298             :  *        -- the "cursor" maintained by the AMI is positioned at the tuple
     299             :  *           returned previously.
     300             :  *
     301             :  *      Initial States:
     302             :  *        -- the relation indicated is opened for TID range scanning.
     303             :  * ----------------------------------------------------------------
     304             :  */
     305             : static TupleTableSlot *
     306        8610 : ExecTidRangeScan(PlanState *pstate)
     307             : {
     308        8610 :     TidRangeScanState *node = castNode(TidRangeScanState, pstate);
     309             : 
     310        8610 :     return ExecScan(&node->ss,
     311             :                     (ExecScanAccessMtd) TidRangeNext,
     312             :                     (ExecScanRecheckMtd) TidRangeRecheck);
     313             : }
     314             : 
     315             : /* ----------------------------------------------------------------
     316             :  *      ExecReScanTidRangeScan(node)
     317             :  * ----------------------------------------------------------------
     318             :  */
     319             : void
     320          66 : ExecReScanTidRangeScan(TidRangeScanState *node)
     321             : {
     322             :     /* mark scan as not in progress, and tid range list as not computed yet */
     323          66 :     node->trss_inScan = false;
     324             : 
     325             :     /*
     326             :      * We must wait until TidRangeNext before calling table_rescan_tidrange.
     327             :      */
     328          66 :     ExecScanReScan(&node->ss);
     329          66 : }
     330             : 
     331             : /* ----------------------------------------------------------------
     332             :  *      ExecEndTidRangeScan
     333             :  *
     334             :  *      Releases any storage allocated through C routines.
     335             :  *      Returns nothing.
     336             :  * ----------------------------------------------------------------
     337             :  */
     338             : void
     339         208 : ExecEndTidRangeScan(TidRangeScanState *node)
     340             : {
     341         208 :     TableScanDesc scan = node->ss.ss_currentScanDesc;
     342             : 
     343         208 :     if (scan != NULL)
     344         116 :         table_endscan(scan);
     345         208 : }
     346             : 
     347             : /* ----------------------------------------------------------------
     348             :  *      ExecInitTidRangeScan
     349             :  *
     350             :  *      Initializes the tid range scan's state information, creates
     351             :  *      scan keys, and opens the scan relation.
     352             :  *
     353             :  *      Parameters:
     354             :  *        node: TidRangeScan node produced by the planner.
     355             :  *        estate: the execution state initialized in InitPlan.
     356             :  * ----------------------------------------------------------------
     357             :  */
     358             : TidRangeScanState *
     359        1946 : ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
     360             : {
     361             :     TidRangeScanState *tidrangestate;
     362             :     Relation    currentRelation;
     363             : 
     364             :     /*
     365             :      * create state structure
     366             :      */
     367        1946 :     tidrangestate = makeNode(TidRangeScanState);
     368        1946 :     tidrangestate->ss.ps.plan = (Plan *) node;
     369        1946 :     tidrangestate->ss.ps.state = estate;
     370        1946 :     tidrangestate->ss.ps.ExecProcNode = ExecTidRangeScan;
     371             : 
     372             :     /*
     373             :      * Miscellaneous initialization
     374             :      *
     375             :      * create expression context for node
     376             :      */
     377        1946 :     ExecAssignExprContext(estate, &tidrangestate->ss.ps);
     378             : 
     379             :     /*
     380             :      * mark scan as not in progress, and TID range as not computed yet
     381             :      */
     382        1946 :     tidrangestate->trss_inScan = false;
     383             : 
     384             :     /*
     385             :      * open the scan relation
     386             :      */
     387        1946 :     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
     388             : 
     389        1946 :     tidrangestate->ss.ss_currentRelation = currentRelation;
     390        1946 :     tidrangestate->ss.ss_currentScanDesc = NULL; /* no table scan here */
     391             : 
     392             :     /*
     393             :      * get the scan type from the relation descriptor.
     394             :      */
     395        1946 :     ExecInitScanTupleSlot(estate, &tidrangestate->ss,
     396             :                           RelationGetDescr(currentRelation),
     397             :                           table_slot_callbacks(currentRelation));
     398             : 
     399             :     /*
     400             :      * Initialize result type and projection.
     401             :      */
     402        1946 :     ExecInitResultTypeTL(&tidrangestate->ss.ps);
     403        1946 :     ExecAssignScanProjectionInfo(&tidrangestate->ss);
     404             : 
     405             :     /*
     406             :      * initialize child expressions
     407             :      */
     408        1946 :     tidrangestate->ss.ps.qual =
     409        1946 :         ExecInitQual(node->scan.plan.qual, (PlanState *) tidrangestate);
     410             : 
     411        1946 :     TidExprListCreate(tidrangestate);
     412             : 
     413             :     /*
     414             :      * all done.
     415             :      */
     416        1946 :     return tidrangestate;
     417             : }

Generated by: LCOV version 1.16