LCOV - code coverage report
Current view: top level - src/backend/executor - nodeIndexonlyscan.c (source / functions) Hit Total Coverage
Test: PostgreSQL 16beta1 Lines: 156 172 90.7 %
Date: 2023-06-06 09:15:10 Functions: 12 13 92.3 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * nodeIndexonlyscan.c
       4             :  *    Routines to support index-only scans
       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/nodeIndexonlyscan.c
      12             :  *
      13             :  *-------------------------------------------------------------------------
      14             :  */
      15             : /*
      16             :  * INTERFACE ROUTINES
      17             :  *      ExecIndexOnlyScan           scans an index
      18             :  *      IndexOnlyNext               retrieve next tuple
      19             :  *      ExecInitIndexOnlyScan       creates and initializes state info.
      20             :  *      ExecReScanIndexOnlyScan     rescans the indexed relation.
      21             :  *      ExecEndIndexOnlyScan        releases all storage.
      22             :  *      ExecIndexOnlyMarkPos        marks scan position.
      23             :  *      ExecIndexOnlyRestrPos       restores scan position.
      24             :  *      ExecIndexOnlyScanEstimate   estimates DSM space needed for
      25             :  *                      parallel index-only scan
      26             :  *      ExecIndexOnlyScanInitializeDSM  initialize DSM for parallel
      27             :  *                      index-only scan
      28             :  *      ExecIndexOnlyScanReInitializeDSM    reinitialize DSM for fresh scan
      29             :  *      ExecIndexOnlyScanInitializeWorker attach to DSM info in parallel worker
      30             :  */
      31             : #include "postgres.h"
      32             : 
      33             : #include "access/genam.h"
      34             : #include "access/relscan.h"
      35             : #include "access/tableam.h"
      36             : #include "access/tupdesc.h"
      37             : #include "access/visibilitymap.h"
      38             : #include "executor/execdebug.h"
      39             : #include "executor/nodeIndexonlyscan.h"
      40             : #include "executor/nodeIndexscan.h"
      41             : #include "miscadmin.h"
      42             : #include "storage/bufmgr.h"
      43             : #include "storage/predicate.h"
      44             : #include "utils/memutils.h"
      45             : #include "utils/rel.h"
      46             : 
      47             : 
      48             : static TupleTableSlot *IndexOnlyNext(IndexOnlyScanState *node);
      49             : static void StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup,
      50             :                             TupleDesc itupdesc);
      51             : 
      52             : 
      53             : /* ----------------------------------------------------------------
      54             :  *      IndexOnlyNext
      55             :  *
      56             :  *      Retrieve a tuple from the IndexOnlyScan node's index.
      57             :  * ----------------------------------------------------------------
      58             :  */
      59             : static TupleTableSlot *
      60     5037160 : IndexOnlyNext(IndexOnlyScanState *node)
      61             : {
      62             :     EState     *estate;
      63             :     ExprContext *econtext;
      64             :     ScanDirection direction;
      65             :     IndexScanDesc scandesc;
      66             :     TupleTableSlot *slot;
      67             :     ItemPointer tid;
      68             : 
      69             :     /*
      70             :      * extract necessary information from index scan node
      71             :      */
      72     5037160 :     estate = node->ss.ps.state;
      73             : 
      74             :     /*
      75             :      * Determine which direction to scan the index in based on the plan's scan
      76             :      * direction and the current direction of execution.
      77             :      */
      78     5037160 :     direction = ScanDirectionCombine(estate->es_direction,
      79             :                                      ((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir);
      80     5037160 :     scandesc = node->ioss_ScanDesc;
      81     5037160 :     econtext = node->ss.ps.ps_ExprContext;
      82     5037160 :     slot = node->ss.ss_ScanTupleSlot;
      83             : 
      84     5037160 :     if (scandesc == NULL)
      85             :     {
      86             :         /*
      87             :          * We reach here if the index only scan is not parallel, or if we're
      88             :          * serially executing an index only scan that was planned to be
      89             :          * parallel.
      90             :          */
      91        9458 :         scandesc = index_beginscan(node->ss.ss_currentRelation,
      92             :                                    node->ioss_RelationDesc,
      93             :                                    estate->es_snapshot,
      94             :                                    node->ioss_NumScanKeys,
      95             :                                    node->ioss_NumOrderByKeys);
      96             : 
      97        9458 :         node->ioss_ScanDesc = scandesc;
      98             : 
      99             : 
     100             :         /* Set it up for index-only scan */
     101        9458 :         node->ioss_ScanDesc->xs_want_itup = true;
     102        9458 :         node->ioss_VMBuffer = InvalidBuffer;
     103             : 
     104             :         /*
     105             :          * If no run-time keys to calculate or they are ready, go ahead and
     106             :          * pass the scankeys to the index AM.
     107             :          */
     108        9458 :         if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     109        9458 :             index_rescan(scandesc,
     110        9458 :                          node->ioss_ScanKeys,
     111             :                          node->ioss_NumScanKeys,
     112        9458 :                          node->ioss_OrderByKeys,
     113             :                          node->ioss_NumOrderByKeys);
     114             :     }
     115             : 
     116             :     /*
     117             :      * OK, now that we have what we need, fetch the next tuple.
     118             :      */
     119     5164124 :     while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
     120             :     {
     121     5084096 :         bool        tuple_from_heap = false;
     122             : 
     123     5084096 :         CHECK_FOR_INTERRUPTS();
     124             : 
     125             :         /*
     126             :          * We can skip the heap fetch if the TID references a heap page on
     127             :          * which all tuples are known visible to everybody.  In any case,
     128             :          * we'll use the index tuple not the heap tuple as the data source.
     129             :          *
     130             :          * Note on Memory Ordering Effects: visibilitymap_get_status does not
     131             :          * lock the visibility map buffer, and therefore the result we read
     132             :          * here could be slightly stale.  However, it can't be stale enough to
     133             :          * matter.
     134             :          *
     135             :          * We need to detect clearing a VM bit due to an insert right away,
     136             :          * because the tuple is present in the index page but not visible. The
     137             :          * reading of the TID by this scan (using a shared lock on the index
     138             :          * buffer) is serialized with the insert of the TID into the index
     139             :          * (using an exclusive lock on the index buffer). Because the VM bit
     140             :          * is cleared before updating the index, and locking/unlocking of the
     141             :          * index page acts as a full memory barrier, we are sure to see the
     142             :          * cleared bit if we see a recently-inserted TID.
     143             :          *
     144             :          * Deletes do not update the index page (only VACUUM will clear out
     145             :          * the TID), so the clearing of the VM bit by a delete is not
     146             :          * serialized with this test below, and we may see a value that is
     147             :          * significantly stale. However, we don't care about the delete right
     148             :          * away, because the tuple is still visible until the deleting
     149             :          * transaction commits or the statement ends (if it's our
     150             :          * transaction). In either case, the lock on the VM buffer will have
     151             :          * been released (acting as a write barrier) after clearing the bit.
     152             :          * And for us to have a snapshot that includes the deleting
     153             :          * transaction (making the tuple invisible), we must have acquired
     154             :          * ProcArrayLock after that time, acting as a read barrier.
     155             :          *
     156             :          * It's worth going through this complexity to avoid needing to lock
     157             :          * the VM buffer, which could cause significant contention.
     158             :          */
     159     5084096 :         if (!VM_ALL_VISIBLE(scandesc->heapRelation,
     160             :                             ItemPointerGetBlockNumber(tid),
     161             :                             &node->ioss_VMBuffer))
     162             :         {
     163             :             /*
     164             :              * Rats, we have to visit the heap to check visibility.
     165             :              */
     166     1940466 :             InstrCountTuples2(node, 1);
     167     1940466 :             if (!index_fetch_heap(scandesc, node->ioss_TableSlot))
     168      126958 :                 continue;       /* no visible tuple, try next index entry */
     169             : 
     170     1813508 :             ExecClearTuple(node->ioss_TableSlot);
     171             : 
     172             :             /*
     173             :              * Only MVCC snapshots are supported here, so there should be no
     174             :              * need to keep following the HOT chain once a visible entry has
     175             :              * been found.  If we did want to allow that, we'd need to keep
     176             :              * more state to remember not to call index_getnext_tid next time.
     177             :              */
     178     1813508 :             if (scandesc->xs_heap_continue)
     179           0 :                 elog(ERROR, "non-MVCC snapshots are not supported in index-only scans");
     180             : 
     181             :             /*
     182             :              * Note: at this point we are holding a pin on the heap page, as
     183             :              * recorded in scandesc->xs_cbuf.  We could release that pin now,
     184             :              * but it's not clear whether it's a win to do so.  The next index
     185             :              * entry might require a visit to the same heap page.
     186             :              */
     187             : 
     188     1813508 :             tuple_from_heap = true;
     189             :         }
     190             : 
     191             :         /*
     192             :          * Fill the scan tuple slot with data from the index.  This might be
     193             :          * provided in either HeapTuple or IndexTuple format.  Conceivably an
     194             :          * index AM might fill both fields, in which case we prefer the heap
     195             :          * format, since it's probably a bit cheaper to fill a slot from.
     196             :          */
     197     4957138 :         if (scandesc->xs_hitup)
     198             :         {
     199             :             /*
     200             :              * We don't take the trouble to verify that the provided tuple has
     201             :              * exactly the slot's format, but it seems worth doing a quick
     202             :              * check on the number of fields.
     203             :              */
     204             :             Assert(slot->tts_tupleDescriptor->natts ==
     205             :                    scandesc->xs_hitupdesc->natts);
     206     1437684 :             ExecForceStoreHeapTuple(scandesc->xs_hitup, slot, false);
     207             :         }
     208     3519454 :         else if (scandesc->xs_itup)
     209     3519454 :             StoreIndexTuple(slot, scandesc->xs_itup, scandesc->xs_itupdesc);
     210             :         else
     211           0 :             elog(ERROR, "no data returned for index-only scan");
     212             : 
     213             :         /*
     214             :          * If the index was lossy, we have to recheck the index quals.
     215             :          */
     216     4957138 :         if (scandesc->xs_recheck)
     217             :         {
     218          14 :             econtext->ecxt_scantuple = slot;
     219          14 :             if (!ExecQualAndReset(node->recheckqual, econtext))
     220             :             {
     221             :                 /* Fails recheck, so drop it and loop back for another */
     222           6 :                 InstrCountFiltered2(node, 1);
     223           6 :                 continue;
     224             :             }
     225             :         }
     226             : 
     227             :         /*
     228             :          * We don't currently support rechecking ORDER BY distances.  (In
     229             :          * principle, if the index can support retrieval of the originally
     230             :          * indexed value, it should be able to produce an exact distance
     231             :          * calculation too.  So it's not clear that adding code here for
     232             :          * recheck/re-sort would be worth the trouble.  But we should at least
     233             :          * throw an error if someone tries it.)
     234             :          */
     235     4957132 :         if (scandesc->numberOfOrderBys > 0 && scandesc->xs_recheckorderby)
     236           6 :             ereport(ERROR,
     237             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     238             :                      errmsg("lossy distance functions are not supported in index-only scans")));
     239             : 
     240             :         /*
     241             :          * If we didn't access the heap, then we'll need to take a predicate
     242             :          * lock explicitly, as if we had.  For now we do that at page level.
     243             :          */
     244     4957126 :         if (!tuple_from_heap)
     245     3143630 :             PredicateLockPage(scandesc->heapRelation,
     246             :                               ItemPointerGetBlockNumber(tid),
     247             :                               estate->es_snapshot);
     248             : 
     249     4957126 :         return slot;
     250             :     }
     251             : 
     252             :     /*
     253             :      * if we get here it means the index scan failed so we are at the end of
     254             :      * the scan..
     255             :      */
     256       80028 :     return ExecClearTuple(slot);
     257             : }
     258             : 
     259             : /*
     260             :  * StoreIndexTuple
     261             :  *      Fill the slot with data from the index tuple.
     262             :  *
     263             :  * At some point this might be generally-useful functionality, but
     264             :  * right now we don't need it elsewhere.
     265             :  */
     266             : static void
     267     3519454 : StoreIndexTuple(TupleTableSlot *slot, IndexTuple itup, TupleDesc itupdesc)
     268             : {
     269             :     /*
     270             :      * Note: we must use the tupdesc supplied by the AM in index_deform_tuple,
     271             :      * not the slot's tupdesc, in case the latter has different datatypes
     272             :      * (this happens for btree name_ops in particular).  They'd better have
     273             :      * the same number of columns though, as well as being datatype-compatible
     274             :      * which is something we can't so easily check.
     275             :      */
     276             :     Assert(slot->tts_tupleDescriptor->natts == itupdesc->natts);
     277             : 
     278     3519454 :     ExecClearTuple(slot);
     279     3519454 :     index_deform_tuple(itup, itupdesc, slot->tts_values, slot->tts_isnull);
     280     3519454 :     ExecStoreVirtualTuple(slot);
     281     3519454 : }
     282             : 
     283             : /*
     284             :  * IndexOnlyRecheck -- access method routine to recheck a tuple in EvalPlanQual
     285             :  *
     286             :  * This can't really happen, since an index can't supply CTID which would
     287             :  * be necessary data for any potential EvalPlanQual target relation.  If it
     288             :  * did happen, the EPQ code would pass us the wrong data, namely a heap
     289             :  * tuple not an index tuple.  So throw an error.
     290             :  */
     291             : static bool
     292           0 : IndexOnlyRecheck(IndexOnlyScanState *node, TupleTableSlot *slot)
     293             : {
     294           0 :     elog(ERROR, "EvalPlanQual recheck is not supported in index-only scans");
     295             :     return false;               /* keep compiler quiet */
     296             : }
     297             : 
     298             : /* ----------------------------------------------------------------
     299             :  *      ExecIndexOnlyScan(node)
     300             :  * ----------------------------------------------------------------
     301             :  */
     302             : static TupleTableSlot *
     303     5036708 : ExecIndexOnlyScan(PlanState *pstate)
     304             : {
     305     5036708 :     IndexOnlyScanState *node = castNode(IndexOnlyScanState, pstate);
     306             : 
     307             :     /*
     308             :      * If we have runtime keys and they've not already been set up, do it now.
     309             :      */
     310     5036708 :     if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
     311         568 :         ExecReScan((PlanState *) node);
     312             : 
     313     5036708 :     return ExecScan(&node->ss,
     314             :                     (ExecScanAccessMtd) IndexOnlyNext,
     315             :                     (ExecScanRecheckMtd) IndexOnlyRecheck);
     316             : }
     317             : 
     318             : /* ----------------------------------------------------------------
     319             :  *      ExecReScanIndexOnlyScan(node)
     320             :  *
     321             :  *      Recalculates the values of any scan keys whose value depends on
     322             :  *      information known at runtime, then rescans the indexed relation.
     323             :  *
     324             :  *      Updating the scan key was formerly done separately in
     325             :  *      ExecUpdateIndexScanKeys. Integrating it into ReScan makes
     326             :  *      rescans of indices and relations/general streams more uniform.
     327             :  * ----------------------------------------------------------------
     328             :  */
     329             : void
     330       82908 : ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
     331             : {
     332             :     /*
     333             :      * If we are doing runtime key calculations (ie, any of the index key
     334             :      * values weren't simple Consts), compute the new key values.  But first,
     335             :      * reset the context so we don't leak memory as each outer tuple is
     336             :      * scanned.  Note this assumes that we will recalculate *all* runtime keys
     337             :      * on each call.
     338             :      */
     339       82908 :     if (node->ioss_NumRuntimeKeys != 0)
     340             :     {
     341       82738 :         ExprContext *econtext = node->ioss_RuntimeContext;
     342             : 
     343       82738 :         ResetExprContext(econtext);
     344       82738 :         ExecIndexEvalRuntimeKeys(econtext,
     345             :                                  node->ioss_RuntimeKeys,
     346             :                                  node->ioss_NumRuntimeKeys);
     347             :     }
     348       82908 :     node->ioss_RuntimeKeysReady = true;
     349             : 
     350             :     /* reset index scan */
     351       82908 :     if (node->ioss_ScanDesc)
     352       81322 :         index_rescan(node->ioss_ScanDesc,
     353       81322 :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     354       81322 :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     355             : 
     356       82908 :     ExecScanReScan(&node->ss);
     357       82908 : }
     358             : 
     359             : 
     360             : /* ----------------------------------------------------------------
     361             :  *      ExecEndIndexOnlyScan
     362             :  * ----------------------------------------------------------------
     363             :  */
     364             : void
     365       14192 : ExecEndIndexOnlyScan(IndexOnlyScanState *node)
     366             : {
     367             :     Relation    indexRelationDesc;
     368             :     IndexScanDesc indexScanDesc;
     369             : 
     370             :     /*
     371             :      * extract information from the node
     372             :      */
     373       14192 :     indexRelationDesc = node->ioss_RelationDesc;
     374       14192 :     indexScanDesc = node->ioss_ScanDesc;
     375             : 
     376             :     /* Release VM buffer pin, if any. */
     377       14192 :     if (node->ioss_VMBuffer != InvalidBuffer)
     378             :     {
     379        5794 :         ReleaseBuffer(node->ioss_VMBuffer);
     380        5794 :         node->ioss_VMBuffer = InvalidBuffer;
     381             :     }
     382             : 
     383             :     /*
     384             :      * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext
     385             :      */
     386             : #ifdef NOT_USED
     387             :     ExecFreeExprContext(&node->ss.ps);
     388             :     if (node->ioss_RuntimeContext)
     389             :         FreeExprContext(node->ioss_RuntimeContext, true);
     390             : #endif
     391             : 
     392             :     /*
     393             :      * clear out tuple table slots
     394             :      */
     395       14192 :     if (node->ss.ps.ps_ResultTupleSlot)
     396        4010 :         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
     397       14192 :     ExecClearTuple(node->ss.ss_ScanTupleSlot);
     398             : 
     399             :     /*
     400             :      * close the index relation (no-op if we didn't open it)
     401             :      */
     402       14192 :     if (indexScanDesc)
     403        9566 :         index_endscan(indexScanDesc);
     404       14192 :     if (indexRelationDesc)
     405       12182 :         index_close(indexRelationDesc, NoLock);
     406       14192 : }
     407             : 
     408             : /* ----------------------------------------------------------------
     409             :  *      ExecIndexOnlyMarkPos
     410             :  *
     411             :  * Note: we assume that no caller attempts to set a mark before having read
     412             :  * at least one tuple.  Otherwise, ioss_ScanDesc might still be NULL.
     413             :  * ----------------------------------------------------------------
     414             :  */
     415             : void
     416      124010 : ExecIndexOnlyMarkPos(IndexOnlyScanState *node)
     417             : {
     418      124010 :     EState     *estate = node->ss.ps.state;
     419      124010 :     EPQState   *epqstate = estate->es_epq_active;
     420             : 
     421      124010 :     if (epqstate != NULL)
     422             :     {
     423             :         /*
     424             :          * We are inside an EvalPlanQual recheck.  If a test tuple exists for
     425             :          * this relation, then we shouldn't access the index at all.  We would
     426             :          * instead need to save, and later restore, the state of the
     427             :          * relsubs_done flag, so that re-fetching the test tuple is possible.
     428             :          * However, given the assumption that no caller sets a mark at the
     429             :          * start of the scan, we can only get here with relsubs_done[i]
     430             :          * already set, and so no state need be saved.
     431             :          */
     432           0 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     433             : 
     434             :         Assert(scanrelid > 0);
     435           0 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     436           0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     437             :         {
     438             :             /* Verify the claim above */
     439           0 :             if (!epqstate->relsubs_done[scanrelid - 1])
     440           0 :                 elog(ERROR, "unexpected ExecIndexOnlyMarkPos call in EPQ recheck");
     441           0 :             return;
     442             :         }
     443             :     }
     444             : 
     445      124010 :     index_markpos(node->ioss_ScanDesc);
     446             : }
     447             : 
     448             : /* ----------------------------------------------------------------
     449             :  *      ExecIndexOnlyRestrPos
     450             :  * ----------------------------------------------------------------
     451             :  */
     452             : void
     453           6 : ExecIndexOnlyRestrPos(IndexOnlyScanState *node)
     454             : {
     455           6 :     EState     *estate = node->ss.ps.state;
     456           6 :     EPQState   *epqstate = estate->es_epq_active;
     457             : 
     458           6 :     if (estate->es_epq_active != NULL)
     459             :     {
     460             :         /* See comments in ExecIndexMarkPos */
     461           0 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     462             : 
     463             :         Assert(scanrelid > 0);
     464           0 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     465           0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     466             :         {
     467             :             /* Verify the claim above */
     468           0 :             if (!epqstate->relsubs_done[scanrelid - 1])
     469           0 :                 elog(ERROR, "unexpected ExecIndexOnlyRestrPos call in EPQ recheck");
     470           0 :             return;
     471             :         }
     472             :     }
     473             : 
     474           6 :     index_restrpos(node->ioss_ScanDesc);
     475             : }
     476             : 
     477             : /* ----------------------------------------------------------------
     478             :  *      ExecInitIndexOnlyScan
     479             :  *
     480             :  *      Initializes the index scan's state information, creates
     481             :  *      scan keys, and opens the base and index relations.
     482             :  *
     483             :  *      Note: index scans have 2 sets of state information because
     484             :  *            we have to keep track of the base relation and the
     485             :  *            index relation.
     486             :  * ----------------------------------------------------------------
     487             :  */
     488             : IndexOnlyScanState *
     489       14324 : ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
     490             : {
     491             :     IndexOnlyScanState *indexstate;
     492             :     Relation    currentRelation;
     493             :     LOCKMODE    lockmode;
     494             :     TupleDesc   tupDesc;
     495             : 
     496             :     /*
     497             :      * create state structure
     498             :      */
     499       14324 :     indexstate = makeNode(IndexOnlyScanState);
     500       14324 :     indexstate->ss.ps.plan = (Plan *) node;
     501       14324 :     indexstate->ss.ps.state = estate;
     502       14324 :     indexstate->ss.ps.ExecProcNode = ExecIndexOnlyScan;
     503             : 
     504             :     /*
     505             :      * Miscellaneous initialization
     506             :      *
     507             :      * create expression context for node
     508             :      */
     509       14324 :     ExecAssignExprContext(estate, &indexstate->ss.ps);
     510             : 
     511             :     /*
     512             :      * open the scan relation
     513             :      */
     514       14324 :     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
     515             : 
     516       14324 :     indexstate->ss.ss_currentRelation = currentRelation;
     517       14324 :     indexstate->ss.ss_currentScanDesc = NULL;    /* no heap scan here */
     518             : 
     519             :     /*
     520             :      * Build the scan tuple type using the indextlist generated by the
     521             :      * planner.  We use this, rather than the index's physical tuple
     522             :      * descriptor, because the latter contains storage column types not the
     523             :      * types of the original datums.  (It's the AM's responsibility to return
     524             :      * suitable data anyway.)
     525             :      */
     526       14324 :     tupDesc = ExecTypeFromTL(node->indextlist);
     527       14324 :     ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc,
     528             :                           &TTSOpsVirtual);
     529             : 
     530             :     /*
     531             :      * We need another slot, in a format that's suitable for the table AM, for
     532             :      * when we need to fetch a tuple from the table for rechecking visibility.
     533             :      */
     534       14324 :     indexstate->ioss_TableSlot =
     535       14324 :         ExecAllocTableSlot(&estate->es_tupleTable,
     536             :                            RelationGetDescr(currentRelation),
     537             :                            table_slot_callbacks(currentRelation));
     538             : 
     539             :     /*
     540             :      * Initialize result type and projection info.  The node's targetlist will
     541             :      * contain Vars with varno = INDEX_VAR, referencing the scan tuple.
     542             :      */
     543       14324 :     ExecInitResultTypeTL(&indexstate->ss.ps);
     544       14324 :     ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
     545             : 
     546             :     /*
     547             :      * initialize child expressions
     548             :      *
     549             :      * Note: we don't initialize all of the indexorderby expression, only the
     550             :      * sub-parts corresponding to runtime keys (see below).
     551             :      */
     552       14324 :     indexstate->ss.ps.qual =
     553       14324 :         ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
     554       14324 :     indexstate->recheckqual =
     555       14324 :         ExecInitQual(node->recheckqual, (PlanState *) indexstate);
     556             : 
     557             :     /*
     558             :      * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
     559             :      * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
     560             :      * references to nonexistent indexes.
     561             :      */
     562       14324 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
     563        2010 :         return indexstate;
     564             : 
     565             :     /* Open the index relation. */
     566       12314 :     lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
     567       12314 :     indexstate->ioss_RelationDesc = index_open(node->indexid, lockmode);
     568             : 
     569             :     /*
     570             :      * Initialize index-specific scan state
     571             :      */
     572       12314 :     indexstate->ioss_RuntimeKeysReady = false;
     573       12314 :     indexstate->ioss_RuntimeKeys = NULL;
     574       12314 :     indexstate->ioss_NumRuntimeKeys = 0;
     575             : 
     576             :     /*
     577             :      * build the index scan keys from the index qualification
     578             :      */
     579       12314 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     580             :                            indexstate->ioss_RelationDesc,
     581             :                            node->indexqual,
     582             :                            false,
     583       12314 :                            &indexstate->ioss_ScanKeys,
     584             :                            &indexstate->ioss_NumScanKeys,
     585             :                            &indexstate->ioss_RuntimeKeys,
     586             :                            &indexstate->ioss_NumRuntimeKeys,
     587             :                            NULL,    /* no ArrayKeys */
     588             :                            NULL);
     589             : 
     590             :     /*
     591             :      * any ORDER BY exprs have to be turned into scankeys in the same way
     592             :      */
     593       12314 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     594             :                            indexstate->ioss_RelationDesc,
     595             :                            node->indexorderby,
     596             :                            true,
     597       12314 :                            &indexstate->ioss_OrderByKeys,
     598             :                            &indexstate->ioss_NumOrderByKeys,
     599             :                            &indexstate->ioss_RuntimeKeys,
     600             :                            &indexstate->ioss_NumRuntimeKeys,
     601             :                            NULL,    /* no ArrayKeys */
     602             :                            NULL);
     603             : 
     604             :     /*
     605             :      * If we have runtime keys, we need an ExprContext to evaluate them. The
     606             :      * node's standard context won't do because we want to reset that context
     607             :      * for every tuple.  So, build another context just like the other one...
     608             :      * -tgl 7/11/00
     609             :      */
     610       12314 :     if (indexstate->ioss_NumRuntimeKeys != 0)
     611             :     {
     612        2020 :         ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
     613             : 
     614        2020 :         ExecAssignExprContext(estate, &indexstate->ss.ps);
     615        2020 :         indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
     616        2020 :         indexstate->ss.ps.ps_ExprContext = stdecontext;
     617             :     }
     618             :     else
     619             :     {
     620       10294 :         indexstate->ioss_RuntimeContext = NULL;
     621             :     }
     622             : 
     623             :     /*
     624             :      * all done.
     625             :      */
     626       12314 :     return indexstate;
     627             : }
     628             : 
     629             : /* ----------------------------------------------------------------
     630             :  *      Parallel Index-only Scan Support
     631             :  * ----------------------------------------------------------------
     632             :  */
     633             : 
     634             : /* ----------------------------------------------------------------
     635             :  *      ExecIndexOnlyScanEstimate
     636             :  *
     637             :  *      Compute the amount of space we'll need in the parallel
     638             :  *      query DSM, and inform pcxt->estimator about our needs.
     639             :  * ----------------------------------------------------------------
     640             :  */
     641             : void
     642          40 : ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
     643             :                           ParallelContext *pcxt)
     644             : {
     645          40 :     EState     *estate = node->ss.ps.state;
     646             : 
     647          40 :     node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
     648             :                                                       estate->es_snapshot);
     649          40 :     shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
     650          40 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     651          40 : }
     652             : 
     653             : /* ----------------------------------------------------------------
     654             :  *      ExecIndexOnlyScanInitializeDSM
     655             :  *
     656             :  *      Set up a parallel index-only scan descriptor.
     657             :  * ----------------------------------------------------------------
     658             :  */
     659             : void
     660          40 : ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
     661             :                                ParallelContext *pcxt)
     662             : {
     663          40 :     EState     *estate = node->ss.ps.state;
     664             :     ParallelIndexScanDesc piscan;
     665             : 
     666          40 :     piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
     667          40 :     index_parallelscan_initialize(node->ss.ss_currentRelation,
     668             :                                   node->ioss_RelationDesc,
     669             :                                   estate->es_snapshot,
     670             :                                   piscan);
     671          40 :     shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
     672          40 :     node->ioss_ScanDesc =
     673          40 :         index_beginscan_parallel(node->ss.ss_currentRelation,
     674             :                                  node->ioss_RelationDesc,
     675             :                                  node->ioss_NumScanKeys,
     676             :                                  node->ioss_NumOrderByKeys,
     677             :                                  piscan);
     678          40 :     node->ioss_ScanDesc->xs_want_itup = true;
     679          40 :     node->ioss_VMBuffer = InvalidBuffer;
     680             : 
     681             :     /*
     682             :      * If no run-time keys to calculate or they are ready, go ahead and pass
     683             :      * the scankeys to the index AM.
     684             :      */
     685          40 :     if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     686          40 :         index_rescan(node->ioss_ScanDesc,
     687          40 :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     688          40 :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     689          40 : }
     690             : 
     691             : /* ----------------------------------------------------------------
     692             :  *      ExecIndexOnlyScanReInitializeDSM
     693             :  *
     694             :  *      Reset shared state before beginning a fresh scan.
     695             :  * ----------------------------------------------------------------
     696             :  */
     697             : void
     698          12 : ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
     699             :                                  ParallelContext *pcxt)
     700             : {
     701          12 :     index_parallelrescan(node->ioss_ScanDesc);
     702          12 : }
     703             : 
     704             : /* ----------------------------------------------------------------
     705             :  *      ExecIndexOnlyScanInitializeWorker
     706             :  *
     707             :  *      Copy relevant information from TOC into planstate.
     708             :  * ----------------------------------------------------------------
     709             :  */
     710             : void
     711         200 : ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
     712             :                                   ParallelWorkerContext *pwcxt)
     713             : {
     714             :     ParallelIndexScanDesc piscan;
     715             : 
     716         200 :     piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
     717         200 :     node->ioss_ScanDesc =
     718         200 :         index_beginscan_parallel(node->ss.ss_currentRelation,
     719             :                                  node->ioss_RelationDesc,
     720             :                                  node->ioss_NumScanKeys,
     721             :                                  node->ioss_NumOrderByKeys,
     722             :                                  piscan);
     723         200 :     node->ioss_ScanDesc->xs_want_itup = true;
     724             : 
     725             :     /*
     726             :      * If no run-time keys to calculate or they are ready, go ahead and pass
     727             :      * the scankeys to the index AM.
     728             :      */
     729         200 :     if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     730         200 :         index_rescan(node->ioss_ScanDesc,
     731         200 :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     732         200 :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     733         200 : }

Generated by: LCOV version 1.14