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

Generated by: LCOV version 1.13