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

Generated by: LCOV version 1.14