LCOV - code coverage report
Current view: top level - src/backend/executor - nodeTidrangescan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 80.3 % 193 155
Test Date: 2026-04-16 02:16:28 Functions: 88.2 % 17 15
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-2026, 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/execParallel.h"
      22              : #include "executor/executor.h"
      23              : #include "executor/instrument.h"
      24              : #include "executor/nodeTidrangescan.h"
      25              : #include "nodes/nodeFuncs.h"
      26              : #include "utils/rel.h"
      27              : 
      28              : 
      29              : /*
      30              :  * It's sufficient to check varattno to identify the CTID variable, as any
      31              :  * Var in the relation scan qual must be for our table.  (Even if it's a
      32              :  * parameterized scan referencing some other table's CTID, the other table's
      33              :  * Var would have become a Param by the time it gets here.)
      34              :  */
      35              : #define IsCTIDVar(node)  \
      36              :     ((node) != NULL && \
      37              :      IsA((node), Var) && \
      38              :      ((Var *) (node))->varattno == SelfItemPointerAttributeNumber)
      39              : 
      40              : typedef enum
      41              : {
      42              :     TIDEXPR_UPPER_BOUND,
      43              :     TIDEXPR_LOWER_BOUND,
      44              : } TidExprType;
      45              : 
      46              : /* Upper or lower range bound for scan */
      47              : typedef struct TidOpExpr
      48              : {
      49              :     TidExprType exprtype;       /* type of op; lower or upper */
      50              :     ExprState  *exprstate;      /* ExprState for a TID-yielding subexpr */
      51              :     bool        inclusive;      /* whether op is inclusive */
      52              : } TidOpExpr;
      53              : 
      54              : /*
      55              :  * For the given 'expr', build and return an appropriate TidOpExpr taking into
      56              :  * account the expr's operator and operand order.
      57              :  */
      58              : static TidOpExpr *
      59         1446 : MakeTidOpExpr(OpExpr *expr, TidRangeScanState *tidstate)
      60              : {
      61         1446 :     Node       *arg1 = get_leftop((Expr *) expr);
      62         1446 :     Node       *arg2 = get_rightop((Expr *) expr);
      63         1446 :     ExprState  *exprstate = NULL;
      64         1446 :     bool        invert = false;
      65              :     TidOpExpr  *tidopexpr;
      66              : 
      67         1446 :     if (IsCTIDVar(arg1))
      68         1422 :         exprstate = ExecInitExpr((Expr *) arg2, &tidstate->ss.ps);
      69           24 :     else if (IsCTIDVar(arg2))
      70              :     {
      71           24 :         exprstate = ExecInitExpr((Expr *) arg1, &tidstate->ss.ps);
      72           24 :         invert = true;
      73              :     }
      74              :     else
      75            0 :         elog(ERROR, "could not identify CTID variable");
      76              : 
      77         1446 :     tidopexpr = palloc_object(TidOpExpr);
      78         1446 :     tidopexpr->inclusive = false;    /* for now */
      79              : 
      80         1446 :     switch (expr->opno)
      81              :     {
      82           27 :         case TIDLessEqOperator:
      83           27 :             tidopexpr->inclusive = true;
      84              :             pg_fallthrough;
      85          163 :         case TIDLessOperator:
      86          163 :             tidopexpr->exprtype = invert ? TIDEXPR_LOWER_BOUND : TIDEXPR_UPPER_BOUND;
      87          163 :             break;
      88         1191 :         case TIDGreaterEqOperator:
      89         1191 :             tidopexpr->inclusive = true;
      90              :             pg_fallthrough;
      91         1283 :         case TIDGreaterOperator:
      92         1283 :             tidopexpr->exprtype = invert ? TIDEXPR_UPPER_BOUND : TIDEXPR_LOWER_BOUND;
      93         1283 :             break;
      94            0 :         default:
      95            0 :             elog(ERROR, "could not identify CTID operator");
      96              :     }
      97              : 
      98         1446 :     tidopexpr->exprstate = exprstate;
      99              : 
     100         1446 :     return tidopexpr;
     101              : }
     102              : 
     103              : /*
     104              :  * Extract the qual subexpressions that yield TIDs to search for,
     105              :  * and compile them into ExprStates if they're ordinary expressions.
     106              :  */
     107              : static void
     108         1397 : TidExprListCreate(TidRangeScanState *tidrangestate)
     109              : {
     110         1397 :     TidRangeScan *node = (TidRangeScan *) tidrangestate->ss.ps.plan;
     111         1397 :     List       *tidexprs = NIL;
     112              :     ListCell   *l;
     113              : 
     114         2843 :     foreach(l, node->tidrangequals)
     115              :     {
     116         1446 :         OpExpr     *opexpr = lfirst(l);
     117              :         TidOpExpr  *tidopexpr;
     118              : 
     119         1446 :         if (!IsA(opexpr, OpExpr))
     120            0 :             elog(ERROR, "could not identify CTID expression");
     121              : 
     122         1446 :         tidopexpr = MakeTidOpExpr(opexpr, tidrangestate);
     123         1446 :         tidexprs = lappend(tidexprs, tidopexpr);
     124              :     }
     125              : 
     126         1397 :     tidrangestate->trss_tidexprs = tidexprs;
     127         1397 : }
     128              : 
     129              : /* ----------------------------------------------------------------
     130              :  *      TidRangeEval
     131              :  *
     132              :  *      Compute and set node's block and offset range to scan by evaluating
     133              :  *      node->trss_tidexprs.  Returns false if we detect the range cannot
     134              :  *      contain any tuples.  Returns true if it's possible for the range to
     135              :  *      contain tuples.  We don't bother validating that trss_mintid is less
     136              :  *      than or equal to trss_maxtid, as the scan_set_tidrange() table AM
     137              :  *      function will handle that.
     138              :  * ----------------------------------------------------------------
     139              :  */
     140              : static bool
     141         1379 : TidRangeEval(TidRangeScanState *node)
     142              : {
     143         1379 :     ExprContext *econtext = node->ss.ps.ps_ExprContext;
     144              :     ItemPointerData lowerBound;
     145              :     ItemPointerData upperBound;
     146              :     ListCell   *l;
     147              : 
     148              :     /*
     149              :      * Set the upper and lower bounds to the absolute limits of the range of
     150              :      * the ItemPointer type.  Below we'll try to narrow this range on either
     151              :      * side by looking at the TidOpExprs.
     152              :      */
     153         1379 :     ItemPointerSet(&lowerBound, 0, 0);
     154         1379 :     ItemPointerSet(&upperBound, InvalidBlockNumber, PG_UINT16_MAX);
     155              : 
     156         2789 :     foreach(l, node->trss_tidexprs)
     157              :     {
     158         1414 :         TidOpExpr  *tidopexpr = (TidOpExpr *) lfirst(l);
     159              :         ItemPointer itemptr;
     160              :         bool        isNull;
     161              : 
     162              :         /* Evaluate this bound. */
     163              :         itemptr = (ItemPointer)
     164         1414 :             DatumGetPointer(ExecEvalExprSwitchContext(tidopexpr->exprstate,
     165              :                                                       econtext,
     166              :                                                       &isNull));
     167              : 
     168              :         /* If the bound is NULL, *nothing* matches the qual. */
     169         1414 :         if (isNull)
     170            4 :             return false;
     171              : 
     172         1410 :         if (tidopexpr->exprtype == TIDEXPR_LOWER_BOUND)
     173              :         {
     174              :             ItemPointerData lb;
     175              : 
     176         1233 :             ItemPointerCopy(itemptr, &lb);
     177              : 
     178              :             /*
     179              :              * Normalize non-inclusive ranges to become inclusive.  The
     180              :              * resulting ItemPointer here may not be a valid item pointer.
     181              :              */
     182         1233 :             if (!tidopexpr->inclusive)
     183           70 :                 ItemPointerInc(&lb);
     184              : 
     185              :             /* Check if we can narrow the range using this qual */
     186         1233 :             if (ItemPointerCompare(&lb, &lowerBound) > 0)
     187         1233 :                 ItemPointerCopy(&lb, &lowerBound);
     188              :         }
     189              : 
     190          177 :         else if (tidopexpr->exprtype == TIDEXPR_UPPER_BOUND)
     191              :         {
     192              :             ItemPointerData ub;
     193              : 
     194          177 :             ItemPointerCopy(itemptr, &ub);
     195              : 
     196              :             /*
     197              :              * Normalize non-inclusive ranges to become inclusive.  The
     198              :              * resulting ItemPointer here may not be a valid item pointer.
     199              :              */
     200          177 :             if (!tidopexpr->inclusive)
     201          102 :                 ItemPointerDec(&ub);
     202              : 
     203              :             /* Check if we can narrow the range using this qual */
     204          177 :             if (ItemPointerCompare(&ub, &upperBound) < 0)
     205          177 :                 ItemPointerCopy(&ub, &upperBound);
     206              :         }
     207              :     }
     208              : 
     209         1375 :     ItemPointerCopy(&lowerBound, &node->trss_mintid);
     210         1375 :     ItemPointerCopy(&upperBound, &node->trss_maxtid);
     211              : 
     212         1375 :     return true;
     213              : }
     214              : 
     215              : /* ----------------------------------------------------------------
     216              :  *      TidRangeNext
     217              :  *
     218              :  *      Retrieve a tuple from the TidRangeScan node's currentRelation
     219              :  *      using the TIDs in the TidRangeScanState information.
     220              :  *
     221              :  * ----------------------------------------------------------------
     222              :  */
     223              : static TupleTableSlot *
     224         6516 : TidRangeNext(TidRangeScanState *node)
     225              : {
     226              :     TableScanDesc scandesc;
     227              :     EState     *estate;
     228              :     ScanDirection direction;
     229              :     TupleTableSlot *slot;
     230              : 
     231              :     /*
     232              :      * extract necessary information from TID scan node
     233              :      */
     234         6516 :     scandesc = node->ss.ss_currentScanDesc;
     235         6516 :     estate = node->ss.ps.state;
     236         6516 :     slot = node->ss.ss_ScanTupleSlot;
     237         6516 :     direction = estate->es_direction;
     238              : 
     239         6516 :     if (!node->trss_inScan)
     240              :     {
     241              :         /* First time through, compute TID range to scan */
     242         1378 :         if (!TidRangeEval(node))
     243            4 :             return NULL;
     244              : 
     245         1374 :         if (scandesc == NULL)
     246              :         {
     247         1234 :             uint32      flags = SO_NONE;
     248              : 
     249         1234 :             if (ScanRelIsReadOnly(&node->ss))
     250         1230 :                 flags |= SO_HINT_REL_READ_ONLY;
     251              : 
     252         1234 :             if (estate->es_instrument & INSTRUMENT_IO)
     253            0 :                 flags |= SO_SCAN_INSTRUMENT;
     254              : 
     255         1234 :             scandesc = table_beginscan_tidrange(node->ss.ss_currentRelation,
     256              :                                                 estate->es_snapshot,
     257              :                                                 &node->trss_mintid,
     258              :                                                 &node->trss_maxtid,
     259              :                                                 flags);
     260         1234 :             node->ss.ss_currentScanDesc = scandesc;
     261              :         }
     262              :         else
     263              :         {
     264              :             /* rescan with the updated TID range */
     265          140 :             table_rescan_tidrange(scandesc, &node->trss_mintid,
     266              :                                   &node->trss_maxtid);
     267              :         }
     268              : 
     269         1374 :         node->trss_inScan = true;
     270              :     }
     271              : 
     272              :     /* Fetch the next tuple. */
     273         6512 :     if (!table_scan_getnextslot_tidrange(scandesc, direction, slot))
     274              :     {
     275          212 :         node->trss_inScan = false;
     276          212 :         ExecClearTuple(slot);
     277              :     }
     278              : 
     279         6502 :     return slot;
     280              : }
     281              : 
     282              : /*
     283              :  * TidRangeRecheck -- access method routine to recheck a tuple in EvalPlanQual
     284              :  */
     285              : static bool
     286            1 : TidRangeRecheck(TidRangeScanState *node, TupleTableSlot *slot)
     287              : {
     288            1 :     if (!TidRangeEval(node))
     289            0 :         return false;
     290              : 
     291              :     Assert(ItemPointerIsValid(&slot->tts_tid));
     292              : 
     293              :     /* Recheck the ctid is still within range */
     294            2 :     if (ItemPointerCompare(&slot->tts_tid, &node->trss_mintid) < 0 ||
     295            1 :         ItemPointerCompare(&slot->tts_tid, &node->trss_maxtid) > 0)
     296            1 :         return false;
     297              : 
     298            0 :     return true;
     299              : }
     300              : 
     301              : /* ----------------------------------------------------------------
     302              :  *      ExecTidRangeScan(node)
     303              :  *
     304              :  *      Scans the relation using tids and returns the next qualifying tuple.
     305              :  *      We call the ExecScan() routine and pass it the appropriate
     306              :  *      access method functions.
     307              :  *
     308              :  *      Conditions:
     309              :  *        -- the "cursor" maintained by the AMI is positioned at the tuple
     310              :  *           returned previously.
     311              :  *
     312              :  *      Initial States:
     313              :  *        -- the relation indicated is opened for TID range scanning.
     314              :  * ----------------------------------------------------------------
     315              :  */
     316              : static TupleTableSlot *
     317         6517 : ExecTidRangeScan(PlanState *pstate)
     318              : {
     319         6517 :     TidRangeScanState *node = castNode(TidRangeScanState, pstate);
     320              : 
     321         6517 :     return ExecScan(&node->ss,
     322              :                     (ExecScanAccessMtd) TidRangeNext,
     323              :                     (ExecScanRecheckMtd) TidRangeRecheck);
     324              : }
     325              : 
     326              : /* ----------------------------------------------------------------
     327              :  *      ExecReScanTidRangeScan(node)
     328              :  * ----------------------------------------------------------------
     329              :  */
     330              : void
     331           64 : ExecReScanTidRangeScan(TidRangeScanState *node)
     332              : {
     333              :     /* mark scan as not in progress, and tid range list as not computed yet */
     334           64 :     node->trss_inScan = false;
     335              : 
     336              :     /*
     337              :      * We must wait until TidRangeNext before calling table_rescan_tidrange.
     338              :      */
     339           64 :     ExecScanReScan(&node->ss);
     340           64 : }
     341              : 
     342              : /* ----------------------------------------------------------------
     343              :  *      ExecEndTidRangeScan
     344              :  *
     345              :  *      Releases any storage allocated through C routines.
     346              :  *      Returns nothing.
     347              :  * ----------------------------------------------------------------
     348              :  */
     349              : void
     350          243 : ExecEndTidRangeScan(TidRangeScanState *node)
     351              : {
     352          243 :     TableScanDesc scan = node->ss.ss_currentScanDesc;
     353              : 
     354              :     /* Collect IO stats for this process into shared instrumentation */
     355          243 :     if (node->trss_sinstrument != NULL && IsParallelWorker())
     356              :     {
     357              :         TidRangeScanInstrumentation *si;
     358              : 
     359              :         Assert(ParallelWorkerNumber < node->trss_sinstrument->num_workers);
     360            0 :         si = &node->trss_sinstrument->sinstrument[ParallelWorkerNumber];
     361              : 
     362            0 :         if (scan && scan->rs_instrument)
     363              :         {
     364            0 :             AccumulateIOStats(&si->stats.io, &scan->rs_instrument->io);
     365              :         }
     366              :     }
     367              : 
     368          243 :     if (scan != NULL)
     369          160 :         table_endscan(scan);
     370          243 : }
     371              : 
     372              : /* ----------------------------------------------------------------
     373              :  *      ExecInitTidRangeScan
     374              :  *
     375              :  *      Initializes the tid range scan's state information, creates
     376              :  *      scan keys, and opens the scan relation.
     377              :  *
     378              :  *      Parameters:
     379              :  *        node: TidRangeScan node produced by the planner.
     380              :  *        estate: the execution state initialized in InitPlan.
     381              :  * ----------------------------------------------------------------
     382              :  */
     383              : TidRangeScanState *
     384         1397 : ExecInitTidRangeScan(TidRangeScan *node, EState *estate, int eflags)
     385              : {
     386              :     TidRangeScanState *tidrangestate;
     387              :     Relation    currentRelation;
     388              : 
     389              :     /*
     390              :      * create state structure
     391              :      */
     392         1397 :     tidrangestate = makeNode(TidRangeScanState);
     393         1397 :     tidrangestate->ss.ps.plan = (Plan *) node;
     394         1397 :     tidrangestate->ss.ps.state = estate;
     395         1397 :     tidrangestate->ss.ps.ExecProcNode = ExecTidRangeScan;
     396              : 
     397              :     /*
     398              :      * Miscellaneous initialization
     399              :      *
     400              :      * create expression context for node
     401              :      */
     402         1397 :     ExecAssignExprContext(estate, &tidrangestate->ss.ps);
     403              : 
     404              :     /*
     405              :      * mark scan as not in progress, and TID range as not computed yet
     406              :      */
     407         1397 :     tidrangestate->trss_inScan = false;
     408              : 
     409              :     /*
     410              :      * open the scan relation
     411              :      */
     412         1397 :     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
     413              : 
     414         1397 :     tidrangestate->ss.ss_currentRelation = currentRelation;
     415         1397 :     tidrangestate->ss.ss_currentScanDesc = NULL; /* no table scan here */
     416              : 
     417              :     /*
     418              :      * get the scan type from the relation descriptor.
     419              :      */
     420         1397 :     ExecInitScanTupleSlot(estate, &tidrangestate->ss,
     421              :                           RelationGetDescr(currentRelation),
     422              :                           table_slot_callbacks(currentRelation),
     423              :                           TTS_FLAG_OBEYS_NOT_NULL_CONSTRAINTS);
     424              : 
     425              :     /*
     426              :      * Initialize result type and projection.
     427              :      */
     428         1397 :     ExecInitResultTypeTL(&tidrangestate->ss.ps);
     429         1397 :     ExecAssignScanProjectionInfo(&tidrangestate->ss);
     430              : 
     431              :     /*
     432              :      * initialize child expressions
     433              :      */
     434         1397 :     tidrangestate->ss.ps.qual =
     435         1397 :         ExecInitQual(node->scan.plan.qual, (PlanState *) tidrangestate);
     436              : 
     437         1397 :     TidExprListCreate(tidrangestate);
     438              : 
     439              :     /*
     440              :      * all done.
     441              :      */
     442         1397 :     return tidrangestate;
     443              : }
     444              : 
     445              : /* ----------------------------------------------------------------
     446              :  *                      Parallel Scan Support
     447              :  * ----------------------------------------------------------------
     448              :  */
     449              : 
     450              : /* ----------------------------------------------------------------
     451              :  *      ExecTidRangeScanEstimate
     452              :  *
     453              :  *      Compute the amount of space we'll need in the parallel
     454              :  *      query DSM, and inform pcxt->estimator about our needs.
     455              :  * ----------------------------------------------------------------
     456              :  */
     457              : void
     458           16 : ExecTidRangeScanEstimate(TidRangeScanState *node, ParallelContext *pcxt)
     459              : {
     460           16 :     EState     *estate = node->ss.ps.state;
     461              : 
     462           16 :     node->trss_pscanlen =
     463           16 :         table_parallelscan_estimate(node->ss.ss_currentRelation,
     464              :                                     estate->es_snapshot);
     465           16 :     shm_toc_estimate_chunk(&pcxt->estimator, node->trss_pscanlen);
     466           16 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     467           16 : }
     468              : 
     469              : /* ----------------------------------------------------------------
     470              :  *      ExecTidRangeScanInitializeDSM
     471              :  *
     472              :  *      Set up a parallel TID range scan descriptor.
     473              :  * ----------------------------------------------------------------
     474              :  */
     475              : void
     476           16 : ExecTidRangeScanInitializeDSM(TidRangeScanState *node, ParallelContext *pcxt)
     477              : {
     478           16 :     EState     *estate = node->ss.ps.state;
     479              :     ParallelTableScanDesc pscan;
     480           16 :     uint32      flags = SO_NONE;
     481              : 
     482           16 :     if (ScanRelIsReadOnly(&node->ss))
     483           16 :         flags |= SO_HINT_REL_READ_ONLY;
     484              : 
     485           16 :     if (estate->es_instrument & INSTRUMENT_IO)
     486            0 :         flags |= SO_SCAN_INSTRUMENT;
     487              : 
     488           16 :     pscan = shm_toc_allocate(pcxt->toc, node->trss_pscanlen);
     489           16 :     table_parallelscan_initialize(node->ss.ss_currentRelation,
     490              :                                   pscan,
     491              :                                   estate->es_snapshot);
     492           16 :     shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
     493           16 :     node->ss.ss_currentScanDesc =
     494           16 :         table_beginscan_parallel_tidrange(node->ss.ss_currentRelation,
     495              :                                           pscan, flags);
     496           16 : }
     497              : 
     498              : /* ----------------------------------------------------------------
     499              :  *      ExecTidRangeScanReInitializeDSM
     500              :  *
     501              :  *      Reset shared state before beginning a fresh scan.
     502              :  * ----------------------------------------------------------------
     503              :  */
     504              : void
     505            0 : ExecTidRangeScanReInitializeDSM(TidRangeScanState *node,
     506              :                                 ParallelContext *pcxt)
     507              : {
     508              :     ParallelTableScanDesc pscan;
     509              : 
     510            0 :     pscan = node->ss.ss_currentScanDesc->rs_parallel;
     511            0 :     table_parallelscan_reinitialize(node->ss.ss_currentRelation, pscan);
     512            0 : }
     513              : 
     514              : /* ----------------------------------------------------------------
     515              :  *      ExecTidRangeScanInitializeWorker
     516              :  *
     517              :  *      Copy relevant information from TOC into planstate.
     518              :  * ----------------------------------------------------------------
     519              :  */
     520              : void
     521           64 : ExecTidRangeScanInitializeWorker(TidRangeScanState *node,
     522              :                                  ParallelWorkerContext *pwcxt)
     523              : {
     524              :     ParallelTableScanDesc pscan;
     525           64 :     uint32      flags = SO_NONE;
     526              : 
     527           64 :     if (ScanRelIsReadOnly(&node->ss))
     528           64 :         flags |= SO_HINT_REL_READ_ONLY;
     529              : 
     530           64 :     if (node->ss.ps.state->es_instrument & INSTRUMENT_IO)
     531            0 :         flags |= SO_SCAN_INSTRUMENT;
     532              : 
     533           64 :     pscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
     534           64 :     node->ss.ss_currentScanDesc =
     535           64 :         table_beginscan_parallel_tidrange(node->ss.ss_currentRelation,
     536              :                                           pscan, flags);
     537           64 : }
     538              : 
     539              : /*
     540              :  * Compute the amount of space we'll need for the shared instrumentation and
     541              :  * inform pcxt->estimator.
     542              :  */
     543              : void
     544           16 : ExecTidRangeScanInstrumentEstimate(TidRangeScanState *node,
     545              :                                    ParallelContext *pcxt)
     546              : {
     547           16 :     EState     *estate = node->ss.ps.state;
     548              :     Size        size;
     549              : 
     550           16 :     if ((estate->es_instrument & INSTRUMENT_IO) == 0 || pcxt->nworkers == 0)
     551           16 :         return;
     552              : 
     553            0 :     size = add_size(offsetof(SharedTidRangeScanInstrumentation, sinstrument),
     554            0 :                     mul_size(pcxt->nworkers, sizeof(TidRangeScanInstrumentation)));
     555              : 
     556            0 :     shm_toc_estimate_chunk(&pcxt->estimator, size);
     557            0 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     558              : }
     559              : 
     560              : /*
     561              :  * Set up parallel scan instrumentation.
     562              :  */
     563              : void
     564           16 : ExecTidRangeScanInstrumentInitDSM(TidRangeScanState *node,
     565              :                                   ParallelContext *pcxt)
     566              : {
     567           16 :     EState     *estate = node->ss.ps.state;
     568              :     SharedTidRangeScanInstrumentation *sinstrument;
     569              :     Size        size;
     570              : 
     571           16 :     if ((estate->es_instrument & INSTRUMENT_IO) == 0 || pcxt->nworkers == 0)
     572           16 :         return;
     573              : 
     574            0 :     size = add_size(offsetof(SharedTidRangeScanInstrumentation, sinstrument),
     575            0 :                     mul_size(pcxt->nworkers, sizeof(TidRangeScanInstrumentation)));
     576            0 :     sinstrument = shm_toc_allocate(pcxt->toc, size);
     577            0 :     memset(sinstrument, 0, size);
     578            0 :     sinstrument->num_workers = pcxt->nworkers;
     579            0 :     shm_toc_insert(pcxt->toc,
     580            0 :                    node->ss.ps.plan->plan_node_id +
     581              :                    PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
     582              :                    sinstrument);
     583            0 :     node->trss_sinstrument = sinstrument;
     584              : }
     585              : 
     586              : /*
     587              :  * Look up and save the location of the shared instrumentation.
     588              :  */
     589              : void
     590           64 : ExecTidRangeScanInstrumentInitWorker(TidRangeScanState *node,
     591              :                                      ParallelWorkerContext *pwcxt)
     592              : {
     593           64 :     EState     *estate = node->ss.ps.state;
     594              : 
     595           64 :     if ((estate->es_instrument & INSTRUMENT_IO) == 0)
     596           64 :         return;
     597              : 
     598            0 :     node->trss_sinstrument = shm_toc_lookup(pwcxt->toc,
     599            0 :                                             node->ss.ps.plan->plan_node_id +
     600              :                                             PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
     601              :                                             false);
     602              : }
     603              : 
     604              : /*
     605              :  * Transfer scan instrumentation from DSM to private memory.
     606              :  */
     607              : void
     608            0 : ExecTidRangeScanRetrieveInstrumentation(TidRangeScanState *node)
     609              : {
     610            0 :     SharedTidRangeScanInstrumentation *sinstrument = node->trss_sinstrument;
     611              :     Size        size;
     612              : 
     613            0 :     if (sinstrument == NULL)
     614            0 :         return;
     615              : 
     616            0 :     size = offsetof(SharedTidRangeScanInstrumentation, sinstrument)
     617            0 :         + sinstrument->num_workers * sizeof(TidRangeScanInstrumentation);
     618              : 
     619            0 :     node->trss_sinstrument = palloc(size);
     620            0 :     memcpy(node->trss_sinstrument, sinstrument, size);
     621              : }
        

Generated by: LCOV version 2.0-1