LCOV - code coverage report
Current view: top level - src/backend/executor - nodeTidscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 151 167 90.4 %
Date: 2024-04-25 08:11:33 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nodeTidscan.c
       4             :  *    Routines to support direct tid scans of relations
       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/nodeTidscan.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : /*
      16             :  * INTERFACE ROUTINES
      17             :  *
      18             :  *      ExecTidScan         scans a relation using tids
      19             :  *      ExecInitTidScan     creates and initializes state info.
      20             :  *      ExecReScanTidScan   rescans the tid relation.
      21             :  *      ExecEndTidScan      releases all storage.
      22             :  */
      23             : #include "postgres.h"
      24             : 
      25             : #include "access/sysattr.h"
      26             : #include "access/tableam.h"
      27             : #include "catalog/pg_type.h"
      28             : #include "executor/executor.h"
      29             : #include "executor/nodeTidscan.h"
      30             : #include "lib/qunique.h"
      31             : #include "miscadmin.h"
      32             : #include "nodes/nodeFuncs.h"
      33             : #include "utils/array.h"
      34             : #include "utils/rel.h"
      35             : 
      36             : 
      37             : /*
      38             :  * It's sufficient to check varattno to identify the CTID variable, as any
      39             :  * Var in the relation scan qual must be for our table.  (Even if it's a
      40             :  * parameterized scan referencing some other table's CTID, the other table's
      41             :  * Var would have become a Param by the time it gets here.)
      42             :  */
      43             : #define IsCTIDVar(node)  \
      44             :     ((node) != NULL && \
      45             :      IsA((node), Var) && \
      46             :      ((Var *) (node))->varattno == SelfItemPointerAttributeNumber)
      47             : 
      48             : /* one element in tss_tidexprs */
      49             : typedef struct TidExpr
      50             : {
      51             :     ExprState  *exprstate;      /* ExprState for a TID-yielding subexpr */
      52             :     bool        isarray;        /* if true, it yields tid[] not just tid */
      53             :     CurrentOfExpr *cexpr;       /* alternatively, we can have CURRENT OF */
      54             : } TidExpr;
      55             : 
      56             : static void TidExprListCreate(TidScanState *tidstate);
      57             : static void TidListEval(TidScanState *tidstate);
      58             : static int  itemptr_comparator(const void *a, const void *b);
      59             : static TupleTableSlot *TidNext(TidScanState *node);
      60             : 
      61             : 
      62             : /*
      63             :  * Extract the qual subexpressions that yield TIDs to search for,
      64             :  * and compile them into ExprStates if they're ordinary expressions.
      65             :  *
      66             :  * CURRENT OF is a special case that we can't compile usefully;
      67             :  * just drop it into the TidExpr list as-is.
      68             :  */
      69             : static void
      70         718 : TidExprListCreate(TidScanState *tidstate)
      71             : {
      72         718 :     TidScan    *node = (TidScan *) tidstate->ss.ps.plan;
      73             :     ListCell   *l;
      74             : 
      75         718 :     tidstate->tss_tidexprs = NIL;
      76         718 :     tidstate->tss_isCurrentOf = false;
      77             : 
      78        1460 :     foreach(l, node->tidquals)
      79             :     {
      80         742 :         Expr       *expr = (Expr *) lfirst(l);
      81         742 :         TidExpr    *tidexpr = (TidExpr *) palloc0(sizeof(TidExpr));
      82             : 
      83         742 :         if (is_opclause(expr))
      84             :         {
      85             :             Node       *arg1;
      86             :             Node       *arg2;
      87             : 
      88         272 :             arg1 = get_leftop(expr);
      89         272 :             arg2 = get_rightop(expr);
      90         272 :             if (IsCTIDVar(arg1))
      91         224 :                 tidexpr->exprstate = ExecInitExpr((Expr *) arg2,
      92             :                                                   &tidstate->ss.ps);
      93          48 :             else if (IsCTIDVar(arg2))
      94          48 :                 tidexpr->exprstate = ExecInitExpr((Expr *) arg1,
      95             :                                                   &tidstate->ss.ps);
      96             :             else
      97           0 :                 elog(ERROR, "could not identify CTID variable");
      98         272 :             tidexpr->isarray = false;
      99             :         }
     100         470 :         else if (expr && IsA(expr, ScalarArrayOpExpr))
     101          30 :         {
     102          30 :             ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
     103             : 
     104             :             Assert(IsCTIDVar(linitial(saex->args)));
     105          30 :             tidexpr->exprstate = ExecInitExpr(lsecond(saex->args),
     106             :                                               &tidstate->ss.ps);
     107          30 :             tidexpr->isarray = true;
     108             :         }
     109         440 :         else if (expr && IsA(expr, CurrentOfExpr))
     110         440 :         {
     111         440 :             CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
     112             : 
     113         440 :             tidexpr->cexpr = cexpr;
     114         440 :             tidstate->tss_isCurrentOf = true;
     115             :         }
     116             :         else
     117           0 :             elog(ERROR, "could not identify CTID expression");
     118             : 
     119         742 :         tidstate->tss_tidexprs = lappend(tidstate->tss_tidexprs, tidexpr);
     120             :     }
     121             : 
     122             :     /* CurrentOfExpr could never appear OR'd with something else */
     123             :     Assert(list_length(tidstate->tss_tidexprs) == 1 ||
     124             :            !tidstate->tss_isCurrentOf);
     125         718 : }
     126             : 
     127             : /*
     128             :  * Compute the list of TIDs to be visited, by evaluating the expressions
     129             :  * for them.
     130             :  *
     131             :  * (The result is actually an array, not a list.)
     132             :  */
     133             : static void
     134         628 : TidListEval(TidScanState *tidstate)
     135             : {
     136         628 :     ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
     137             :     TableScanDesc scan;
     138             :     ItemPointerData *tidList;
     139             :     int         numAllocTids;
     140             :     int         numTids;
     141             :     ListCell   *l;
     142             : 
     143             :     /*
     144             :      * Start scan on-demand - initializing a scan isn't free (e.g. heap stats
     145             :      * the size of the table), so it makes sense to delay that until needed -
     146             :      * the node might never get executed.
     147             :      */
     148         628 :     if (tidstate->ss.ss_currentScanDesc == NULL)
     149         622 :         tidstate->ss.ss_currentScanDesc =
     150         622 :             table_beginscan_tid(tidstate->ss.ss_currentRelation,
     151         622 :                                 tidstate->ss.ps.state->es_snapshot);
     152         628 :     scan = tidstate->ss.ss_currentScanDesc;
     153             : 
     154             :     /*
     155             :      * We initialize the array with enough slots for the case that all quals
     156             :      * are simple OpExprs or CurrentOfExprs.  If there are any
     157             :      * ScalarArrayOpExprs, we may have to enlarge the array.
     158             :      */
     159         628 :     numAllocTids = list_length(tidstate->tss_tidexprs);
     160             :     tidList = (ItemPointerData *)
     161         628 :         palloc(numAllocTids * sizeof(ItemPointerData));
     162         628 :     numTids = 0;
     163             : 
     164        1208 :     foreach(l, tidstate->tss_tidexprs)
     165             :     {
     166         640 :         TidExpr    *tidexpr = (TidExpr *) lfirst(l);
     167             :         ItemPointer itemptr;
     168             :         bool        isNull;
     169             : 
     170         640 :         if (tidexpr->exprstate && !tidexpr->isarray)
     171             :         {
     172             :             itemptr = (ItemPointer)
     173         230 :                 DatumGetPointer(ExecEvalExprSwitchContext(tidexpr->exprstate,
     174             :                                                           econtext,
     175             :                                                           &isNull));
     176         230 :             if (isNull)
     177           0 :                 continue;
     178             : 
     179             :             /*
     180             :              * We silently discard any TIDs that the AM considers invalid
     181             :              * (E.g. for heap, they could be out of range at the time of scan
     182             :              * start.  Since we hold at least AccessShareLock on the table, it
     183             :              * won't be possible for someone to truncate away the blocks we
     184             :              * intend to visit.).
     185             :              */
     186         230 :             if (!table_tuple_tid_valid(scan, itemptr))
     187           0 :                 continue;
     188             : 
     189         230 :             if (numTids >= numAllocTids)
     190             :             {
     191           6 :                 numAllocTids *= 2;
     192             :                 tidList = (ItemPointerData *)
     193           6 :                     repalloc(tidList,
     194             :                              numAllocTids * sizeof(ItemPointerData));
     195             :             }
     196         230 :             tidList[numTids++] = *itemptr;
     197             :         }
     198         410 :         else if (tidexpr->exprstate && tidexpr->isarray)
     199          24 :         {
     200             :             Datum       arraydatum;
     201             :             ArrayType  *itemarray;
     202             :             Datum      *ipdatums;
     203             :             bool       *ipnulls;
     204             :             int         ndatums;
     205             :             int         i;
     206             : 
     207          24 :             arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate,
     208             :                                                    econtext,
     209             :                                                    &isNull);
     210          24 :             if (isNull)
     211           0 :                 continue;
     212          24 :             itemarray = DatumGetArrayTypeP(arraydatum);
     213          24 :             deconstruct_array_builtin(itemarray, TIDOID, &ipdatums, &ipnulls, &ndatums);
     214          24 :             if (numTids + ndatums > numAllocTids)
     215             :             {
     216          18 :                 numAllocTids = numTids + ndatums;
     217             :                 tidList = (ItemPointerData *)
     218          18 :                     repalloc(tidList,
     219             :                              numAllocTids * sizeof(ItemPointerData));
     220             :             }
     221          72 :             for (i = 0; i < ndatums; i++)
     222             :             {
     223          48 :                 if (ipnulls[i])
     224           0 :                     continue;
     225             : 
     226          48 :                 itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
     227             : 
     228          48 :                 if (!table_tuple_tid_valid(scan, itemptr))
     229           0 :                     continue;
     230             : 
     231          48 :                 tidList[numTids++] = *itemptr;
     232             :             }
     233          24 :             pfree(ipdatums);
     234          24 :             pfree(ipnulls);
     235             :         }
     236             :         else
     237             :         {
     238             :             ItemPointerData cursor_tid;
     239             : 
     240             :             Assert(tidexpr->cexpr);
     241         326 :             if (execCurrentOf(tidexpr->cexpr, econtext,
     242         386 :                               RelationGetRelid(tidstate->ss.ss_currentRelation),
     243             :                               &cursor_tid))
     244             :             {
     245         276 :                 if (numTids >= numAllocTids)
     246             :                 {
     247           0 :                     numAllocTids *= 2;
     248             :                     tidList = (ItemPointerData *)
     249           0 :                         repalloc(tidList,
     250             :                                  numAllocTids * sizeof(ItemPointerData));
     251             :                 }
     252         276 :                 tidList[numTids++] = cursor_tid;
     253             :             }
     254             :         }
     255             :     }
     256             : 
     257             :     /*
     258             :      * Sort the array of TIDs into order, and eliminate duplicates.
     259             :      * Eliminating duplicates is necessary since we want OR semantics across
     260             :      * the list.  Sorting makes it easier to detect duplicates, and as a bonus
     261             :      * ensures that we will visit the heap in the most efficient way.
     262             :      */
     263         568 :     if (numTids > 1)
     264             :     {
     265             :         /* CurrentOfExpr could never appear OR'd with something else */
     266             :         Assert(!tidstate->tss_isCurrentOf);
     267             : 
     268          30 :         qsort(tidList, numTids, sizeof(ItemPointerData),
     269             :               itemptr_comparator);
     270          30 :         numTids = qunique(tidList, numTids, sizeof(ItemPointerData),
     271             :                           itemptr_comparator);
     272             :     }
     273             : 
     274         568 :     tidstate->tss_TidList = tidList;
     275         568 :     tidstate->tss_NumTids = numTids;
     276         568 :     tidstate->tss_TidPtr = -1;
     277         568 : }
     278             : 
     279             : /*
     280             :  * qsort comparator for ItemPointerData items
     281             :  */
     282             : static int
     283          78 : itemptr_comparator(const void *a, const void *b)
     284             : {
     285          78 :     const ItemPointerData *ipa = (const ItemPointerData *) a;
     286          78 :     const ItemPointerData *ipb = (const ItemPointerData *) b;
     287          78 :     BlockNumber ba = ItemPointerGetBlockNumber(ipa);
     288          78 :     BlockNumber bb = ItemPointerGetBlockNumber(ipb);
     289          78 :     OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
     290          78 :     OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
     291             : 
     292          78 :     if (ba < bb)
     293           0 :         return -1;
     294          78 :     if (ba > bb)
     295           0 :         return 1;
     296          78 :     if (oa < ob)
     297          24 :         return -1;
     298          54 :     if (oa > ob)
     299          54 :         return 1;
     300           0 :     return 0;
     301             : }
     302             : 
     303             : /* ----------------------------------------------------------------
     304             :  *      TidNext
     305             :  *
     306             :  *      Retrieve a tuple from the TidScan node's currentRelation
     307             :  *      using the tids in the TidScanState information.
     308             :  *
     309             :  * ----------------------------------------------------------------
     310             :  */
     311             : static TupleTableSlot *
     312        1146 : TidNext(TidScanState *node)
     313             : {
     314             :     EState     *estate;
     315             :     ScanDirection direction;
     316             :     Snapshot    snapshot;
     317             :     TableScanDesc scan;
     318             :     Relation    heapRelation;
     319             :     TupleTableSlot *slot;
     320             :     ItemPointerData *tidList;
     321             :     int         numTids;
     322             :     bool        bBackward;
     323             : 
     324             :     /*
     325             :      * extract necessary information from tid scan node
     326             :      */
     327        1146 :     estate = node->ss.ps.state;
     328        1146 :     direction = estate->es_direction;
     329        1146 :     snapshot = estate->es_snapshot;
     330        1146 :     heapRelation = node->ss.ss_currentRelation;
     331        1146 :     slot = node->ss.ss_ScanTupleSlot;
     332             : 
     333             :     /*
     334             :      * First time through, compute the list of TIDs to be visited
     335             :      */
     336        1146 :     if (node->tss_TidList == NULL)
     337         628 :         TidListEval(node);
     338             : 
     339        1086 :     scan = node->ss.ss_currentScanDesc;
     340        1086 :     tidList = node->tss_TidList;
     341        1086 :     numTids = node->tss_NumTids;
     342             : 
     343             :     /*
     344             :      * Initialize or advance scan position, depending on direction.
     345             :      */
     346        1086 :     bBackward = ScanDirectionIsBackward(direction);
     347        1086 :     if (bBackward)
     348             :     {
     349           6 :         if (node->tss_TidPtr < 0)
     350             :         {
     351             :             /* initialize for backward scan */
     352           0 :             node->tss_TidPtr = numTids - 1;
     353             :         }
     354             :         else
     355           6 :             node->tss_TidPtr--;
     356             :     }
     357             :     else
     358             :     {
     359        1080 :         if (node->tss_TidPtr < 0)
     360             :         {
     361             :             /* initialize for forward scan */
     362         568 :             node->tss_TidPtr = 0;
     363             :         }
     364             :         else
     365         512 :             node->tss_TidPtr++;
     366             :     }
     367             : 
     368        1116 :     while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
     369             :     {
     370         554 :         ItemPointerData tid = tidList[node->tss_TidPtr];
     371             : 
     372             :         /*
     373             :          * For WHERE CURRENT OF, the tuple retrieved from the cursor might
     374             :          * since have been updated; if so, we should fetch the version that is
     375             :          * current according to our snapshot.
     376             :          */
     377         554 :         if (node->tss_isCurrentOf)
     378         276 :             table_tuple_get_latest_tid(scan, &tid);
     379             : 
     380         554 :         if (table_tuple_fetch_row_version(heapRelation, &tid, snapshot, slot))
     381         524 :             return slot;
     382             : 
     383             :         /* Bad TID or failed snapshot qual; try next */
     384          30 :         if (bBackward)
     385           0 :             node->tss_TidPtr--;
     386             :         else
     387          30 :             node->tss_TidPtr++;
     388             : 
     389          30 :         CHECK_FOR_INTERRUPTS();
     390             :     }
     391             : 
     392             :     /*
     393             :      * if we get here it means the tid scan failed so we are at the end of the
     394             :      * scan..
     395             :      */
     396         562 :     return ExecClearTuple(slot);
     397             : }
     398             : 
     399             : /*
     400             :  * TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
     401             :  */
     402             : static bool
     403           0 : TidRecheck(TidScanState *node, TupleTableSlot *slot)
     404             : {
     405             :     /*
     406             :      * XXX shouldn't we check here to make sure tuple matches TID list? In
     407             :      * runtime-key case this is not certain, is it?  However, in the WHERE
     408             :      * CURRENT OF case it might not match anyway ...
     409             :      */
     410           0 :     return true;
     411             : }
     412             : 
     413             : 
     414             : /* ----------------------------------------------------------------
     415             :  *      ExecTidScan(node)
     416             :  *
     417             :  *      Scans the relation using tids and returns
     418             :  *         the next qualifying tuple in the direction specified.
     419             :  *      We call the ExecScan() routine and pass it the appropriate
     420             :  *      access method functions.
     421             :  *
     422             :  *      Conditions:
     423             :  *        -- the "cursor" maintained by the AMI is positioned at the tuple
     424             :  *           returned previously.
     425             :  *
     426             :  *      Initial States:
     427             :  *        -- the relation indicated is opened for scanning so that the
     428             :  *           "cursor" is positioned before the first qualifying tuple.
     429             :  *        -- tss_TidPtr is -1.
     430             :  * ----------------------------------------------------------------
     431             :  */
     432             : static TupleTableSlot *
     433        1128 : ExecTidScan(PlanState *pstate)
     434             : {
     435        1128 :     TidScanState *node = castNode(TidScanState, pstate);
     436             : 
     437        1128 :     return ExecScan(&node->ss,
     438             :                     (ExecScanAccessMtd) TidNext,
     439             :                     (ExecScanRecheckMtd) TidRecheck);
     440             : }
     441             : 
     442             : /* ----------------------------------------------------------------
     443             :  *      ExecReScanTidScan(node)
     444             :  * ----------------------------------------------------------------
     445             :  */
     446             : void
     447          18 : ExecReScanTidScan(TidScanState *node)
     448             : {
     449          18 :     if (node->tss_TidList)
     450           6 :         pfree(node->tss_TidList);
     451          18 :     node->tss_TidList = NULL;
     452          18 :     node->tss_NumTids = 0;
     453          18 :     node->tss_TidPtr = -1;
     454             : 
     455             :     /* not really necessary, but seems good form */
     456          18 :     if (node->ss.ss_currentScanDesc)
     457           6 :         table_rescan(node->ss.ss_currentScanDesc, NULL);
     458             : 
     459          18 :     ExecScanReScan(&node->ss);
     460          18 : }
     461             : 
     462             : /* ----------------------------------------------------------------
     463             :  *      ExecEndTidScan
     464             :  *
     465             :  *      Releases any storage allocated through C routines.
     466             :  *      Returns nothing.
     467             :  * ----------------------------------------------------------------
     468             :  */
     469             : void
     470         598 : ExecEndTidScan(TidScanState *node)
     471             : {
     472         598 :     if (node->ss.ss_currentScanDesc)
     473         550 :         table_endscan(node->ss.ss_currentScanDesc);
     474         598 : }
     475             : 
     476             : /* ----------------------------------------------------------------
     477             :  *      ExecInitTidScan
     478             :  *
     479             :  *      Initializes the tid scan's state information, creates
     480             :  *      scan keys, and opens the base and tid relations.
     481             :  *
     482             :  *      Parameters:
     483             :  *        node: TidScan node produced by the planner.
     484             :  *        estate: the execution state initialized in InitPlan.
     485             :  * ----------------------------------------------------------------
     486             :  */
     487             : TidScanState *
     488         718 : ExecInitTidScan(TidScan *node, EState *estate, int eflags)
     489             : {
     490             :     TidScanState *tidstate;
     491             :     Relation    currentRelation;
     492             : 
     493             :     /*
     494             :      * create state structure
     495             :      */
     496         718 :     tidstate = makeNode(TidScanState);
     497         718 :     tidstate->ss.ps.plan = (Plan *) node;
     498         718 :     tidstate->ss.ps.state = estate;
     499         718 :     tidstate->ss.ps.ExecProcNode = ExecTidScan;
     500             : 
     501             :     /*
     502             :      * Miscellaneous initialization
     503             :      *
     504             :      * create expression context for node
     505             :      */
     506         718 :     ExecAssignExprContext(estate, &tidstate->ss.ps);
     507             : 
     508             :     /*
     509             :      * mark tid list as not computed yet
     510             :      */
     511         718 :     tidstate->tss_TidList = NULL;
     512         718 :     tidstate->tss_NumTids = 0;
     513         718 :     tidstate->tss_TidPtr = -1;
     514             : 
     515             :     /*
     516             :      * open the scan relation
     517             :      */
     518         718 :     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
     519             : 
     520         718 :     tidstate->ss.ss_currentRelation = currentRelation;
     521         718 :     tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
     522             : 
     523             :     /*
     524             :      * get the scan type from the relation descriptor.
     525             :      */
     526         718 :     ExecInitScanTupleSlot(estate, &tidstate->ss,
     527             :                           RelationGetDescr(currentRelation),
     528             :                           table_slot_callbacks(currentRelation));
     529             : 
     530             :     /*
     531             :      * Initialize result type and projection.
     532             :      */
     533         718 :     ExecInitResultTypeTL(&tidstate->ss.ps);
     534         718 :     ExecAssignScanProjectionInfo(&tidstate->ss);
     535             : 
     536             :     /*
     537             :      * initialize child expressions
     538             :      */
     539         718 :     tidstate->ss.ps.qual =
     540         718 :         ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
     541             : 
     542         718 :     TidExprListCreate(tidstate);
     543             : 
     544             :     /*
     545             :      * all done.
     546             :      */
     547         718 :     return tidstate;
     548             : }

Generated by: LCOV version 1.14