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

Generated by: LCOV version 1.14