LCOV - differential code coverage report
Current view: top level - src/backend/executor - nodeIndexonlyscan.c (source / functions) Coverage Total Hit UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 89.8 % 176 158 11 7 1 104 1 52 10 103 3
Current Date: 2023-04-08 15:15:32 Functions: 92.3 % 13 12 1 11 1 1 11
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

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

Generated by: LCOV version v1.16-55-g56c0a2a