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

Generated by: LCOV version 1.14