LCOV - code coverage report
Current view: top level - src/backend/executor - nodeIndexonlyscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 83.1 % 213 177
Test Date: 2026-02-28 23:15:01 Functions: 78.6 % 14 11
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-2026, 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      2782621 : 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      2782621 :     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      2782621 :     direction = ScanDirectionCombine(estate->es_direction,
      80              :                                      ((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir);
      81      2782621 :     scandesc = node->ioss_ScanDesc;
      82      2782621 :     econtext = node->ss.ps.ps_ExprContext;
      83      2782621 :     slot = node->ss.ss_ScanTupleSlot;
      84              : 
      85      2782621 :     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         5335 :         scandesc = index_beginscan(node->ss.ss_currentRelation,
      93              :                                    node->ioss_RelationDesc,
      94              :                                    estate->es_snapshot,
      95              :                                    &node->ioss_Instrument,
      96              :                                    node->ioss_NumScanKeys,
      97              :                                    node->ioss_NumOrderByKeys);
      98              : 
      99         5335 :         node->ioss_ScanDesc = scandesc;
     100              : 
     101              : 
     102              :         /* Set it up for index-only scan */
     103         5335 :         node->ioss_ScanDesc->xs_want_itup = true;
     104         5335 :         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         5335 :         if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     111         5335 :             index_rescan(scandesc,
     112              :                          node->ioss_ScanKeys,
     113              :                          node->ioss_NumScanKeys,
     114              :                          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      2851960 :     while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
     122              :     {
     123      2750376 :         bool        tuple_from_heap = false;
     124              : 
     125      2750376 :         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      2750376 :         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       978376 :             InstrCountTuples2(node, 1);
     169       978376 :             if (!index_fetch_heap(scandesc, node->ioss_TableSlot))
     170        69336 :                 continue;       /* no visible tuple, try next index entry */
     171              : 
     172       909040 :             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       909040 :             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       909040 :             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      2681040 :         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       718842 :             ExecForceStoreHeapTuple(scandesc->xs_hitup, slot, false);
     209              :         }
     210      1962198 :         else if (scandesc->xs_itup)
     211      1962198 :             StoreIndexTuple(node, 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              :          */
     218      2681040 :         if (scandesc->xs_recheck)
     219              :         {
     220            7 :             econtext->ecxt_scantuple = slot;
     221            7 :             if (!ExecQualAndReset(node->recheckqual, econtext))
     222              :             {
     223              :                 /* Fails recheck, so drop it and loop back for another */
     224            3 :                 InstrCountFiltered2(node, 1);
     225            3 :                 continue;
     226              :             }
     227              :         }
     228              : 
     229              :         /*
     230              :          * We don't currently support rechecking ORDER BY distances.  (In
     231              :          * principle, if the index can support retrieval of the originally
     232              :          * indexed value, it should be able to produce an exact distance
     233              :          * calculation too.  So it's not clear that adding code here for
     234              :          * recheck/re-sort would be worth the trouble.  But we should at least
     235              :          * throw an error if someone tries it.)
     236              :          */
     237      2681037 :         if (scandesc->numberOfOrderBys > 0 && scandesc->xs_recheckorderby)
     238            3 :             ereport(ERROR,
     239              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     240              :                      errmsg("lossy distance functions are not supported in index-only scans")));
     241              : 
     242              :         /*
     243              :          * If we didn't access the heap, then we'll need to take a predicate
     244              :          * lock explicitly, as if we had.  For now we do that at page level.
     245              :          */
     246      2681034 :         if (!tuple_from_heap)
     247      1772000 :             PredicateLockPage(scandesc->heapRelation,
     248              :                               ItemPointerGetBlockNumber(tid),
     249              :                               estate->es_snapshot);
     250              : 
     251      2681034 :         return slot;
     252              :     }
     253              : 
     254              :     /*
     255              :      * if we get here it means the index scan failed so we are at the end of
     256              :      * the scan..
     257              :      */
     258       101584 :     return ExecClearTuple(slot);
     259              : }
     260              : 
     261              : /*
     262              :  * StoreIndexTuple
     263              :  *      Fill the slot with data from the index tuple.
     264              :  *
     265              :  * At some point this might be generally-useful functionality, but
     266              :  * right now we don't need it elsewhere.
     267              :  */
     268              : static void
     269      1962198 : StoreIndexTuple(IndexOnlyScanState *node, TupleTableSlot *slot,
     270              :                 IndexTuple itup, TupleDesc itupdesc)
     271              : {
     272              :     /*
     273              :      * Note: we must use the tupdesc supplied by the AM in index_deform_tuple,
     274              :      * not the slot's tupdesc, in case the latter has different datatypes
     275              :      * (this happens for btree name_ops in particular).  They'd better have
     276              :      * the same number of columns though, as well as being datatype-compatible
     277              :      * which is something we can't so easily check.
     278              :      */
     279              :     Assert(slot->tts_tupleDescriptor->natts == itupdesc->natts);
     280              : 
     281      1962198 :     ExecClearTuple(slot);
     282      1962198 :     index_deform_tuple(itup, itupdesc, slot->tts_values, slot->tts_isnull);
     283              : 
     284              :     /*
     285              :      * Copy all name columns stored as cstrings back into a NAMEDATALEN byte
     286              :      * sized allocation.  We mark this branch as unlikely as generally "name"
     287              :      * is used only for the system catalogs and this would have to be a user
     288              :      * query running on those or some other user table with an index on a name
     289              :      * column.
     290              :      */
     291      1962198 :     if (unlikely(node->ioss_NameCStringAttNums != NULL))
     292              :     {
     293         1842 :         int         attcount = node->ioss_NameCStringCount;
     294              : 
     295         3684 :         for (int idx = 0; idx < attcount; idx++)
     296              :         {
     297         1842 :             int         attnum = node->ioss_NameCStringAttNums[idx];
     298              :             Name        name;
     299              : 
     300              :             /* skip null Datums */
     301         1842 :             if (slot->tts_isnull[attnum])
     302            0 :                 continue;
     303              : 
     304              :             /* allocate the NAMEDATALEN and copy the datum into that memory */
     305         1842 :             name = (Name) MemoryContextAlloc(node->ss.ps.ps_ExprContext->ecxt_per_tuple_memory,
     306              :                                              NAMEDATALEN);
     307              : 
     308              :             /* use namestrcpy to zero-pad all trailing bytes */
     309         1842 :             namestrcpy(name, DatumGetCString(slot->tts_values[attnum]));
     310         1842 :             slot->tts_values[attnum] = NameGetDatum(name);
     311              :         }
     312              :     }
     313              : 
     314      1962198 :     ExecStoreVirtualTuple(slot);
     315      1962198 : }
     316              : 
     317              : /*
     318              :  * IndexOnlyRecheck -- access method routine to recheck a tuple in EvalPlanQual
     319              :  *
     320              :  * This can't really happen, since an index can't supply CTID which would
     321              :  * be necessary data for any potential EvalPlanQual target relation.  If it
     322              :  * did happen, the EPQ code would pass us the wrong data, namely a heap
     323              :  * tuple not an index tuple.  So throw an error.
     324              :  */
     325              : static bool
     326            0 : IndexOnlyRecheck(IndexOnlyScanState *node, TupleTableSlot *slot)
     327              : {
     328            0 :     elog(ERROR, "EvalPlanQual recheck is not supported in index-only scans");
     329              :     return false;               /* keep compiler quiet */
     330              : }
     331              : 
     332              : /* ----------------------------------------------------------------
     333              :  *      ExecIndexOnlyScan(node)
     334              :  * ----------------------------------------------------------------
     335              :  */
     336              : static TupleTableSlot *
     337      2632242 : ExecIndexOnlyScan(PlanState *pstate)
     338              : {
     339      2632242 :     IndexOnlyScanState *node = castNode(IndexOnlyScanState, pstate);
     340              : 
     341              :     /*
     342              :      * If we have runtime keys and they've not already been set up, do it now.
     343              :      */
     344      2632242 :     if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
     345          277 :         ExecReScan((PlanState *) node);
     346              : 
     347      2632242 :     return ExecScan(&node->ss,
     348              :                     (ExecScanAccessMtd) IndexOnlyNext,
     349              :                     (ExecScanRecheckMtd) IndexOnlyRecheck);
     350              : }
     351              : 
     352              : /* ----------------------------------------------------------------
     353              :  *      ExecReScanIndexOnlyScan(node)
     354              :  *
     355              :  *      Recalculates the values of any scan keys whose value depends on
     356              :  *      information known at runtime, then rescans the indexed relation.
     357              :  *
     358              :  *      Updating the scan key was formerly done separately in
     359              :  *      ExecUpdateIndexScanKeys. Integrating it into ReScan makes
     360              :  *      rescans of indices and relations/general streams more uniform.
     361              :  * ----------------------------------------------------------------
     362              :  */
     363              : void
     364       111839 : ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
     365              : {
     366              :     /*
     367              :      * If we are doing runtime key calculations (ie, any of the index key
     368              :      * values weren't simple Consts), compute the new key values.  But first,
     369              :      * reset the context so we don't leak memory as each outer tuple is
     370              :      * scanned.  Note this assumes that we will recalculate *all* runtime keys
     371              :      * on each call.
     372              :      */
     373       111839 :     if (node->ioss_NumRuntimeKeys != 0)
     374              :     {
     375       111739 :         ExprContext *econtext = node->ioss_RuntimeContext;
     376              : 
     377       111739 :         ResetExprContext(econtext);
     378       111739 :         ExecIndexEvalRuntimeKeys(econtext,
     379              :                                  node->ioss_RuntimeKeys,
     380              :                                  node->ioss_NumRuntimeKeys);
     381              :     }
     382       111839 :     node->ioss_RuntimeKeysReady = true;
     383              : 
     384              :     /* reset index scan */
     385       111839 :     if (node->ioss_ScanDesc)
     386       110737 :         index_rescan(node->ioss_ScanDesc,
     387              :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     388              :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     389              : 
     390       111839 :     ExecScanReScan(&node->ss);
     391       111839 : }
     392              : 
     393              : 
     394              : /* ----------------------------------------------------------------
     395              :  *      ExecEndIndexOnlyScan
     396              :  * ----------------------------------------------------------------
     397              :  */
     398              : void
     399         9966 : ExecEndIndexOnlyScan(IndexOnlyScanState *node)
     400              : {
     401              :     Relation    indexRelationDesc;
     402              :     IndexScanDesc indexScanDesc;
     403              : 
     404              :     /*
     405              :      * extract information from the node
     406              :      */
     407         9966 :     indexRelationDesc = node->ioss_RelationDesc;
     408         9966 :     indexScanDesc = node->ioss_ScanDesc;
     409              : 
     410              :     /* Release VM buffer pin, if any. */
     411         9966 :     if (node->ioss_VMBuffer != InvalidBuffer)
     412              :     {
     413         3198 :         ReleaseBuffer(node->ioss_VMBuffer);
     414         3198 :         node->ioss_VMBuffer = InvalidBuffer;
     415              :     }
     416              : 
     417              :     /*
     418              :      * When ending a parallel worker, copy the statistics gathered by the
     419              :      * worker back into shared memory so that it can be picked up by the main
     420              :      * process to report in EXPLAIN ANALYZE
     421              :      */
     422         9966 :     if (node->ioss_SharedInfo != NULL && IsParallelWorker())
     423              :     {
     424              :         IndexScanInstrumentation *winstrument;
     425              : 
     426              :         Assert(ParallelWorkerNumber <= node->ioss_SharedInfo->num_workers);
     427            0 :         winstrument = &node->ioss_SharedInfo->winstrument[ParallelWorkerNumber];
     428              : 
     429              :         /*
     430              :          * We have to accumulate the stats rather than performing a memcpy.
     431              :          * When a Gather/GatherMerge node finishes it will perform planner
     432              :          * shutdown on the workers.  On rescan it will spin up new workers
     433              :          * which will have a new IndexOnlyScanState and zeroed stats.
     434              :          */
     435            0 :         winstrument->nsearches += node->ioss_Instrument.nsearches;
     436              :     }
     437              : 
     438              :     /*
     439              :      * close the index relation (no-op if we didn't open it)
     440              :      */
     441         9966 :     if (indexScanDesc)
     442         5436 :         index_endscan(indexScanDesc);
     443         9966 :     if (indexRelationDesc)
     444         8619 :         index_close(indexRelationDesc, NoLock);
     445         9966 : }
     446              : 
     447              : /* ----------------------------------------------------------------
     448              :  *      ExecIndexOnlyMarkPos
     449              :  *
     450              :  * Note: we assume that no caller attempts to set a mark before having read
     451              :  * at least one tuple.  Otherwise, ioss_ScanDesc might still be NULL.
     452              :  * ----------------------------------------------------------------
     453              :  */
     454              : void
     455        62019 : ExecIndexOnlyMarkPos(IndexOnlyScanState *node)
     456              : {
     457        62019 :     EState     *estate = node->ss.ps.state;
     458        62019 :     EPQState   *epqstate = estate->es_epq_active;
     459              : 
     460        62019 :     if (epqstate != NULL)
     461              :     {
     462              :         /*
     463              :          * We are inside an EvalPlanQual recheck.  If a test tuple exists for
     464              :          * this relation, then we shouldn't access the index at all.  We would
     465              :          * instead need to save, and later restore, the state of the
     466              :          * relsubs_done flag, so that re-fetching the test tuple is possible.
     467              :          * However, given the assumption that no caller sets a mark at the
     468              :          * start of the scan, we can only get here with relsubs_done[i]
     469              :          * already set, and so no state need be saved.
     470              :          */
     471            0 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     472              : 
     473              :         Assert(scanrelid > 0);
     474            0 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     475            0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     476              :         {
     477              :             /* Verify the claim above */
     478            0 :             if (!epqstate->relsubs_done[scanrelid - 1])
     479            0 :                 elog(ERROR, "unexpected ExecIndexOnlyMarkPos call in EPQ recheck");
     480            0 :             return;
     481              :         }
     482              :     }
     483              : 
     484        62019 :     index_markpos(node->ioss_ScanDesc);
     485              : }
     486              : 
     487              : /* ----------------------------------------------------------------
     488              :  *      ExecIndexOnlyRestrPos
     489              :  * ----------------------------------------------------------------
     490              :  */
     491              : void
     492            0 : ExecIndexOnlyRestrPos(IndexOnlyScanState *node)
     493              : {
     494            0 :     EState     *estate = node->ss.ps.state;
     495            0 :     EPQState   *epqstate = estate->es_epq_active;
     496              : 
     497            0 :     if (estate->es_epq_active != NULL)
     498              :     {
     499              :         /* See comments in ExecIndexMarkPos */
     500            0 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     501              : 
     502              :         Assert(scanrelid > 0);
     503            0 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     504            0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     505              :         {
     506              :             /* Verify the claim above */
     507            0 :             if (!epqstate->relsubs_done[scanrelid - 1])
     508            0 :                 elog(ERROR, "unexpected ExecIndexOnlyRestrPos call in EPQ recheck");
     509            0 :             return;
     510              :         }
     511              :     }
     512              : 
     513            0 :     index_restrpos(node->ioss_ScanDesc);
     514              : }
     515              : 
     516              : /* ----------------------------------------------------------------
     517              :  *      ExecInitIndexOnlyScan
     518              :  *
     519              :  *      Initializes the index scan's state information, creates
     520              :  *      scan keys, and opens the base and index relations.
     521              :  *
     522              :  *      Note: index scans have 2 sets of state information because
     523              :  *            we have to keep track of the base relation and the
     524              :  *            index relation.
     525              :  * ----------------------------------------------------------------
     526              :  */
     527              : IndexOnlyScanState *
     528         9991 : ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
     529              : {
     530              :     IndexOnlyScanState *indexstate;
     531              :     Relation    currentRelation;
     532              :     Relation    indexRelation;
     533              :     LOCKMODE    lockmode;
     534              :     TupleDesc   tupDesc;
     535              :     int         indnkeyatts;
     536              :     int         namecount;
     537              : 
     538              :     /*
     539              :      * create state structure
     540              :      */
     541         9991 :     indexstate = makeNode(IndexOnlyScanState);
     542         9991 :     indexstate->ss.ps.plan = (Plan *) node;
     543         9991 :     indexstate->ss.ps.state = estate;
     544         9991 :     indexstate->ss.ps.ExecProcNode = ExecIndexOnlyScan;
     545              : 
     546              :     /*
     547              :      * Miscellaneous initialization
     548              :      *
     549              :      * create expression context for node
     550              :      */
     551         9991 :     ExecAssignExprContext(estate, &indexstate->ss.ps);
     552              : 
     553              :     /*
     554              :      * open the scan relation
     555              :      */
     556         9991 :     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
     557              : 
     558         9991 :     indexstate->ss.ss_currentRelation = currentRelation;
     559         9991 :     indexstate->ss.ss_currentScanDesc = NULL;    /* no heap scan here */
     560              : 
     561              :     /*
     562              :      * Build the scan tuple type using the indextlist generated by the
     563              :      * planner.  We use this, rather than the index's physical tuple
     564              :      * descriptor, because the latter contains storage column types not the
     565              :      * types of the original datums.  (It's the AM's responsibility to return
     566              :      * suitable data anyway.)
     567              :      */
     568         9991 :     tupDesc = ExecTypeFromTL(node->indextlist);
     569         9991 :     ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc,
     570              :                           &TTSOpsVirtual);
     571              : 
     572              :     /*
     573              :      * We need another slot, in a format that's suitable for the table AM, for
     574              :      * when we need to fetch a tuple from the table for rechecking visibility.
     575              :      */
     576         9991 :     indexstate->ioss_TableSlot =
     577         9991 :         ExecAllocTableSlot(&estate->es_tupleTable,
     578              :                            RelationGetDescr(currentRelation),
     579              :                            table_slot_callbacks(currentRelation));
     580              : 
     581              :     /*
     582              :      * Initialize result type and projection info.  The node's targetlist will
     583              :      * contain Vars with varno = INDEX_VAR, referencing the scan tuple.
     584              :      */
     585         9991 :     ExecInitResultTypeTL(&indexstate->ss.ps);
     586         9991 :     ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
     587              : 
     588              :     /*
     589              :      * initialize child expressions
     590              :      *
     591              :      * Note: we don't initialize all of the indexorderby expression, only the
     592              :      * sub-parts corresponding to runtime keys (see below).
     593              :      */
     594         9991 :     indexstate->ss.ps.qual =
     595         9991 :         ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
     596         9991 :     indexstate->recheckqual =
     597         9991 :         ExecInitQual(node->recheckqual, (PlanState *) indexstate);
     598              : 
     599              :     /*
     600              :      * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
     601              :      * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
     602              :      * references to nonexistent indexes.
     603              :      */
     604         9991 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
     605         1347 :         return indexstate;
     606              : 
     607              :     /* Open the index relation. */
     608         8644 :     lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
     609         8644 :     indexRelation = index_open(node->indexid, lockmode);
     610         8644 :     indexstate->ioss_RelationDesc = indexRelation;
     611              : 
     612              :     /*
     613              :      * Initialize index-specific scan state
     614              :      */
     615         8644 :     indexstate->ioss_RuntimeKeysReady = false;
     616         8644 :     indexstate->ioss_RuntimeKeys = NULL;
     617         8644 :     indexstate->ioss_NumRuntimeKeys = 0;
     618              : 
     619              :     /*
     620              :      * build the index scan keys from the index qualification
     621              :      */
     622         8644 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     623              :                            indexRelation,
     624              :                            node->indexqual,
     625              :                            false,
     626         8644 :                            &indexstate->ioss_ScanKeys,
     627              :                            &indexstate->ioss_NumScanKeys,
     628              :                            &indexstate->ioss_RuntimeKeys,
     629              :                            &indexstate->ioss_NumRuntimeKeys,
     630              :                            NULL,    /* no ArrayKeys */
     631              :                            NULL);
     632              : 
     633              :     /*
     634              :      * any ORDER BY exprs have to be turned into scankeys in the same way
     635              :      */
     636         8644 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     637              :                            indexRelation,
     638              :                            node->indexorderby,
     639              :                            true,
     640         8644 :                            &indexstate->ioss_OrderByKeys,
     641              :                            &indexstate->ioss_NumOrderByKeys,
     642              :                            &indexstate->ioss_RuntimeKeys,
     643              :                            &indexstate->ioss_NumRuntimeKeys,
     644              :                            NULL,    /* no ArrayKeys */
     645              :                            NULL);
     646              : 
     647              :     /*
     648              :      * If we have runtime keys, we need an ExprContext to evaluate them. The
     649              :      * node's standard context won't do because we want to reset that context
     650              :      * for every tuple.  So, build another context just like the other one...
     651              :      * -tgl 7/11/00
     652              :      */
     653         8644 :     if (indexstate->ioss_NumRuntimeKeys != 0)
     654              :     {
     655         2654 :         ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
     656              : 
     657         2654 :         ExecAssignExprContext(estate, &indexstate->ss.ps);
     658         2654 :         indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
     659         2654 :         indexstate->ss.ps.ps_ExprContext = stdecontext;
     660              :     }
     661              :     else
     662              :     {
     663         5990 :         indexstate->ioss_RuntimeContext = NULL;
     664              :     }
     665              : 
     666         8644 :     indexstate->ioss_NameCStringAttNums = NULL;
     667         8644 :     indnkeyatts = indexRelation->rd_index->indnkeyatts;
     668         8644 :     namecount = 0;
     669              : 
     670              :     /*
     671              :      * The "name" type for btree uses text_ops which results in storing
     672              :      * cstrings in the indexed keys rather than names.  Here we detect that in
     673              :      * a generic way in case other index AMs want to do the same optimization.
     674              :      * Check for opclasses with an opcintype of NAMEOID and an index tuple
     675              :      * descriptor with CSTRINGOID.  If any of these are found, create an array
     676              :      * marking the index attribute number of each of them.  StoreIndexTuple()
     677              :      * handles copying the name Datums into a NAMEDATALEN-byte allocation.
     678              :      */
     679              : 
     680              :     /* First, count the number of such index keys */
     681        20308 :     for (int attnum = 0; attnum < indnkeyatts; attnum++)
     682              :     {
     683        11664 :         if (TupleDescAttr(indexRelation->rd_att, attnum)->atttypid == CSTRINGOID &&
     684         1706 :             indexRelation->rd_opcintype[attnum] == NAMEOID)
     685         1706 :             namecount++;
     686              :     }
     687              : 
     688         8644 :     if (namecount > 0)
     689              :     {
     690         1706 :         int         idx = 0;
     691              : 
     692              :         /*
     693              :          * Now create an array to mark the attribute numbers of the keys that
     694              :          * need to be converted from cstring to name.
     695              :          */
     696         1706 :         indexstate->ioss_NameCStringAttNums = palloc_array(AttrNumber, namecount);
     697              : 
     698         5157 :         for (int attnum = 0; attnum < indnkeyatts; attnum++)
     699              :         {
     700         3451 :             if (TupleDescAttr(indexRelation->rd_att, attnum)->atttypid == CSTRINGOID &&
     701         1706 :                 indexRelation->rd_opcintype[attnum] == NAMEOID)
     702         1706 :                 indexstate->ioss_NameCStringAttNums[idx++] = (AttrNumber) attnum;
     703              :         }
     704              :     }
     705              : 
     706         8644 :     indexstate->ioss_NameCStringCount = namecount;
     707              : 
     708              :     /*
     709              :      * all done.
     710              :      */
     711         8644 :     return indexstate;
     712              : }
     713              : 
     714              : /* ----------------------------------------------------------------
     715              :  *      Parallel Index-only Scan Support
     716              :  * ----------------------------------------------------------------
     717              :  */
     718              : 
     719              : /* ----------------------------------------------------------------
     720              :  *      ExecIndexOnlyScanEstimate
     721              :  *
     722              :  *      Compute the amount of space we'll need in the parallel
     723              :  *      query DSM, and inform pcxt->estimator about our needs.
     724              :  * ----------------------------------------------------------------
     725              :  */
     726              : void
     727           29 : ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
     728              :                           ParallelContext *pcxt)
     729              : {
     730           29 :     EState     *estate = node->ss.ps.state;
     731           29 :     bool        instrument = (node->ss.ps.instrument != NULL);
     732           29 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
     733              : 
     734           29 :     if (!instrument && !parallel_aware)
     735              :     {
     736              :         /* No DSM required by the scan */
     737            6 :         return;
     738              :     }
     739              : 
     740           23 :     node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
     741              :                                                       node->ioss_NumScanKeys,
     742              :                                                       node->ioss_NumOrderByKeys,
     743              :                                                       estate->es_snapshot,
     744              :                                                       instrument, parallel_aware,
     745              :                                                       pcxt->nworkers);
     746           23 :     shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
     747           23 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     748              : }
     749              : 
     750              : /* ----------------------------------------------------------------
     751              :  *      ExecIndexOnlyScanInitializeDSM
     752              :  *
     753              :  *      Set up a parallel index-only scan descriptor.
     754              :  * ----------------------------------------------------------------
     755              :  */
     756              : void
     757           29 : ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
     758              :                                ParallelContext *pcxt)
     759              : {
     760           29 :     EState     *estate = node->ss.ps.state;
     761              :     ParallelIndexScanDesc piscan;
     762           29 :     bool        instrument = node->ss.ps.instrument != NULL;
     763           29 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
     764              : 
     765           29 :     if (!instrument && !parallel_aware)
     766              :     {
     767              :         /* No DSM required by the scan */
     768            6 :         return;
     769              :     }
     770              : 
     771           23 :     piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
     772           23 :     index_parallelscan_initialize(node->ss.ss_currentRelation,
     773              :                                   node->ioss_RelationDesc,
     774              :                                   estate->es_snapshot,
     775              :                                   instrument, parallel_aware, pcxt->nworkers,
     776              :                                   &node->ioss_SharedInfo, piscan);
     777           23 :     shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
     778              : 
     779           23 :     if (!parallel_aware)
     780              :     {
     781              :         /* Only here to initialize SharedInfo in DSM */
     782            0 :         return;
     783              :     }
     784              : 
     785           23 :     node->ioss_ScanDesc =
     786           23 :         index_beginscan_parallel(node->ss.ss_currentRelation,
     787              :                                  node->ioss_RelationDesc,
     788              :                                  &node->ioss_Instrument,
     789              :                                  node->ioss_NumScanKeys,
     790              :                                  node->ioss_NumOrderByKeys,
     791              :                                  piscan);
     792           23 :     node->ioss_ScanDesc->xs_want_itup = true;
     793           23 :     node->ioss_VMBuffer = InvalidBuffer;
     794              : 
     795              :     /*
     796              :      * If no run-time keys to calculate or they are ready, go ahead and pass
     797              :      * the scankeys to the index AM.
     798              :      */
     799           23 :     if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     800           23 :         index_rescan(node->ioss_ScanDesc,
     801              :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     802              :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     803              : }
     804              : 
     805              : /* ----------------------------------------------------------------
     806              :  *      ExecIndexOnlyScanReInitializeDSM
     807              :  *
     808              :  *      Reset shared state before beginning a fresh scan.
     809              :  * ----------------------------------------------------------------
     810              :  */
     811              : void
     812            6 : ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
     813              :                                  ParallelContext *pcxt)
     814              : {
     815              :     Assert(node->ss.ps.plan->parallel_aware);
     816            6 :     index_parallelrescan(node->ioss_ScanDesc);
     817            6 : }
     818              : 
     819              : /* ----------------------------------------------------------------
     820              :  *      ExecIndexOnlyScanInitializeWorker
     821              :  *
     822              :  *      Copy relevant information from TOC into planstate.
     823              :  * ----------------------------------------------------------------
     824              :  */
     825              : void
     826          121 : ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
     827              :                                   ParallelWorkerContext *pwcxt)
     828              : {
     829              :     ParallelIndexScanDesc piscan;
     830          121 :     bool        instrument = node->ss.ps.instrument != NULL;
     831          121 :     bool        parallel_aware = node->ss.ps.plan->parallel_aware;
     832              : 
     833          121 :     if (!instrument && !parallel_aware)
     834              :     {
     835              :         /* No DSM required by the scan */
     836           18 :         return;
     837              :     }
     838              : 
     839          103 :     piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
     840              : 
     841          103 :     if (instrument)
     842            0 :         node->ioss_SharedInfo = (SharedIndexScanInstrumentation *)
     843            0 :             OffsetToPointer(piscan, piscan->ps_offset_ins);
     844              : 
     845          103 :     if (!parallel_aware)
     846              :     {
     847              :         /* Only here to set up worker node's SharedInfo */
     848            0 :         return;
     849              :     }
     850              : 
     851          103 :     node->ioss_ScanDesc =
     852          103 :         index_beginscan_parallel(node->ss.ss_currentRelation,
     853              :                                  node->ioss_RelationDesc,
     854              :                                  &node->ioss_Instrument,
     855              :                                  node->ioss_NumScanKeys,
     856              :                                  node->ioss_NumOrderByKeys,
     857              :                                  piscan);
     858          103 :     node->ioss_ScanDesc->xs_want_itup = true;
     859              : 
     860              :     /*
     861              :      * If no run-time keys to calculate or they are ready, go ahead and pass
     862              :      * the scankeys to the index AM.
     863              :      */
     864          103 :     if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     865          103 :         index_rescan(node->ioss_ScanDesc,
     866              :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     867              :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     868              : }
     869              : 
     870              : /* ----------------------------------------------------------------
     871              :  *      ExecIndexOnlyScanRetrieveInstrumentation
     872              :  *
     873              :  *      Transfer index-only scan statistics from DSM to private memory.
     874              :  * ----------------------------------------------------------------
     875              :  */
     876              : void
     877            0 : ExecIndexOnlyScanRetrieveInstrumentation(IndexOnlyScanState *node)
     878              : {
     879            0 :     SharedIndexScanInstrumentation *SharedInfo = node->ioss_SharedInfo;
     880              :     size_t      size;
     881              : 
     882            0 :     if (SharedInfo == NULL)
     883            0 :         return;
     884              : 
     885              :     /* Create a copy of SharedInfo in backend-local memory */
     886            0 :     size = offsetof(SharedIndexScanInstrumentation, winstrument) +
     887            0 :         SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
     888            0 :     node->ioss_SharedInfo = palloc(size);
     889            0 :     memcpy(node->ioss_SharedInfo, SharedInfo, size);
     890              : }
        

Generated by: LCOV version 2.0-1