LCOV - code coverage report
Current view: top level - src/backend/executor - nodeIndexonlyscan.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 78.9 % 227 179
Test Date: 2026-09-11 13:15:47 Functions: 82.4 % 17 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 58.3 % 132 77

             Branch data     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/htup_details.h"
      35                 :             : #include "access/relscan.h"
      36                 :             : #include "access/tableam.h"
      37                 :             : #include "access/tupdesc.h"
      38                 :             : #include "access/visibilitymap.h"
      39                 :             : #include "catalog/pg_type.h"
      40                 :             : #include "executor/executor.h"
      41                 :             : #include "executor/instrument.h"
      42                 :             : #include "executor/nodeIndexonlyscan.h"
      43                 :             : #include "executor/nodeIndexscan.h"
      44                 :             : #include "miscadmin.h"
      45                 :             : #include "storage/bufmgr.h"
      46                 :             : #include "storage/predicate.h"
      47                 :             : #include "utils/builtins.h"
      48                 :             : #include "utils/rel.h"
      49                 :             : 
      50                 :             : 
      51                 :             : static TupleTableSlot *IndexOnlyNext(IndexOnlyScanState *node);
      52                 :             : static void StoreIndexTuple(IndexOnlyScanState *node, TupleTableSlot *slot,
      53                 :             :                             IndexScanDesc scandesc);
      54                 :             : 
      55                 :             : 
      56                 :             : /* ----------------------------------------------------------------
      57                 :             :  *      IndexOnlyNext
      58                 :             :  *
      59                 :             :  *      Retrieve a tuple from the IndexOnlyScan node's index.
      60                 :             :  * ----------------------------------------------------------------
      61                 :             :  */
      62                 :             : static TupleTableSlot *
      63                 :     3950435 : IndexOnlyNext(IndexOnlyScanState *node)
      64                 :             : {
      65                 :             :     EState     *estate;
      66                 :             :     ExprContext *econtext;
      67                 :             :     ScanDirection direction;
      68                 :             :     IndexScanDesc scandesc;
      69                 :             :     TupleTableSlot *slot;
      70                 :             :     ItemPointer tid;
      71                 :             : 
      72                 :             :     /*
      73                 :             :      * extract necessary information from index scan node
      74                 :             :      */
      75                 :     3950435 :     estate = node->ss.ps.state;
      76                 :             : 
      77                 :             :     /*
      78                 :             :      * Determine which direction to scan the index in based on the plan's scan
      79                 :             :      * direction and the current direction of execution.
      80                 :             :      */
      81                 :     3950435 :     direction = ScanDirectionCombine(estate->es_direction,
      82                 :             :                                      ((IndexOnlyScan *) node->ss.ps.plan)->indexorderdir);
      83                 :     3950435 :     scandesc = node->ioss_ScanDesc;
      84                 :     3950435 :     econtext = node->ss.ps.ps_ExprContext;
      85                 :     3950435 :     slot = node->ss.ss_ScanTupleSlot;
      86                 :             : 
      87         [ +  + ]:     3950435 :     if (scandesc == NULL)
      88                 :             :     {
      89                 :             :         /*
      90                 :             :          * We reach here if the index only scan is not parallel, or if we're
      91                 :             :          * serially executing an index only scan that was planned to be
      92                 :             :          * parallel.
      93                 :             :          */
      94         [ +  - ]:        6756 :         scandesc = index_beginscan(node->ss.ss_currentRelation,
      95                 :             :                                    node->ioss_RelationDesc,
      96                 :             :                                    estate->es_snapshot,
      97                 :             :                                    node->ioss_Instrument,
      98                 :             :                                    node->ioss_NumScanKeys,
      99                 :             :                                    node->ioss_NumOrderByKeys,
     100                 :        6756 :                                    ScanRelIsReadOnly(&node->ss) ?
     101                 :             :                                    SO_HINT_REL_READ_ONLY : SO_NONE);
     102                 :             : 
     103                 :        6756 :         node->ioss_ScanDesc = scandesc;
     104                 :             : 
     105                 :             : 
     106                 :             :         /* Set it up for index-only scan */
     107                 :        6756 :         node->ioss_ScanDesc->xs_want_itup = true;
     108                 :        6756 :         node->ioss_VMBuffer = InvalidBuffer;
     109                 :             : 
     110                 :             :         /*
     111                 :             :          * If no run-time keys to calculate or they are ready, go ahead and
     112                 :             :          * pass the scankeys to the index AM.
     113                 :             :          */
     114   [ +  +  +  - ]:        6756 :         if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     115                 :        6756 :             index_rescan(scandesc,
     116                 :             :                          node->ioss_ScanKeys,
     117                 :             :                          node->ioss_NumScanKeys,
     118                 :             :                          node->ioss_OrderByKeys,
     119                 :             :                          node->ioss_NumOrderByKeys);
     120                 :             :     }
     121                 :             : 
     122                 :             :     /*
     123                 :             :      * OK, now that we have what we need, fetch the next tuple.
     124                 :             :      */
     125         [ +  + ]:     4029725 :     while ((tid = index_getnext_tid(scandesc, direction)) != NULL)
     126                 :             :     {
     127                 :     3898849 :         bool        tuple_from_heap = false;
     128                 :             : 
     129         [ -  + ]:     3898849 :         CHECK_FOR_INTERRUPTS();
     130                 :             : 
     131                 :             :         /*
     132                 :             :          * We can skip the heap fetch if the TID references a heap page on
     133                 :             :          * which all tuples are known visible to everybody.  In any case,
     134                 :             :          * we'll use the index tuple not the heap tuple as the data source.
     135                 :             :          *
     136                 :             :          * Note on Memory Ordering Effects: visibilitymap_get_status does not
     137                 :             :          * lock the visibility map buffer, and therefore the result we read
     138                 :             :          * here could be slightly stale.  However, it can't be stale enough to
     139                 :             :          * matter; see comments above visibilitymap_get_status for the full
     140                 :             :          * argument.  It's worth going through this complexity to avoid
     141                 :             :          * needing to lock the VM buffer, which could cause significant
     142                 :             :          * contention.
     143                 :             :          */
     144         [ +  + ]:     3898849 :         if (!VM_ALL_VISIBLE(scandesc->heapRelation,
     145                 :             :                             ItemPointerGetBlockNumber(tid),
     146                 :             :                             &node->ioss_VMBuffer))
     147                 :             :         {
     148                 :             :             /*
     149                 :             :              * Rats, we have to visit the heap to check visibility.
     150                 :             :              */
     151         [ +  + ]:      589756 :             InstrCountTuples2(node, 1);
     152         [ +  + ]:      589756 :             if (!index_fetch_heap(scandesc, node->ioss_TableSlot))
     153                 :       79286 :                 continue;       /* no visible tuple, try next index entry */
     154                 :             : 
     155                 :      510470 :             ExecClearTuple(node->ioss_TableSlot);
     156                 :             : 
     157                 :             :             /*
     158                 :             :              * Only MVCC snapshots are supported here, so there should be no
     159                 :             :              * need to keep following the HOT chain once a visible entry has
     160                 :             :              * been found.  If we did want to allow that, we'd need to keep
     161                 :             :              * more state to remember not to call index_getnext_tid next time.
     162                 :             :              */
     163         [ -  + ]:      510470 :             if (scandesc->xs_heap_continue)
     164         [ #  # ]:           0 :                 elog(ERROR, "non-MVCC snapshots are not supported in index-only scans");
     165                 :             : 
     166                 :             :             /*
     167                 :             :              * Note: at this point we are holding a pin on the heap page, as
     168                 :             :              * recorded in scandesc->xs_cbuf.  We could release that pin now,
     169                 :             :              * but it's not clear whether it's a win to do so.  The next index
     170                 :             :              * entry might require a visit to the same heap page.
     171                 :             :              */
     172                 :             : 
     173                 :      510470 :             tuple_from_heap = true;
     174                 :             :         }
     175                 :             : 
     176                 :             :         /* Fill the scan tuple slot with data from the index */
     177                 :     3819563 :         StoreIndexTuple(node, slot, scandesc);
     178                 :             : 
     179                 :             :         /*
     180                 :             :          * If the index was lossy, we have to recheck the index quals.
     181                 :             :          */
     182         [ +  + ]:     3819563 :         if (scandesc->xs_recheck)
     183                 :             :         {
     184                 :           9 :             econtext->ecxt_scantuple = slot;
     185         [ +  + ]:           9 :             if (!ExecQualAndReset(node->recheckqual, econtext))
     186                 :             :             {
     187                 :             :                 /* Fails recheck, so drop it and loop back for another */
     188         [ -  + ]:           4 :                 InstrCountFiltered2(node, 1);
     189                 :           4 :                 continue;
     190                 :             :             }
     191                 :             :         }
     192                 :             : 
     193                 :             :         /*
     194                 :             :          * We don't currently support rechecking ORDER BY distances.  (In
     195                 :             :          * principle, if the index can support retrieval of the originally
     196                 :             :          * indexed value, it should be able to produce an exact distance
     197                 :             :          * calculation too.  So it's not clear that adding code here for
     198                 :             :          * recheck/re-sort would be worth the trouble.  But we should at least
     199                 :             :          * throw an error if someone tries it.)
     200                 :             :          */
     201   [ +  +  +  + ]:     3819559 :         if (scandesc->numberOfOrderBys > 0 && scandesc->xs_recheckorderby)
     202         [ +  - ]:           4 :             ereport(ERROR,
     203                 :             :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     204                 :             :                      errmsg("lossy distance functions are not supported in index-only scans")));
     205                 :             : 
     206                 :             :         /*
     207                 :             :          * If we didn't access the heap, then we'll need to take a predicate
     208                 :             :          * lock explicitly, as if we had.  For now we do that at page level.
     209                 :             :          */
     210         [ +  + ]:     3819555 :         if (!tuple_from_heap)
     211                 :     3309093 :             PredicateLockPage(scandesc->heapRelation,
     212                 :             :                               ItemPointerGetBlockNumber(tid),
     213                 :             :                               estate->es_snapshot);
     214                 :             : 
     215                 :     3819555 :         return slot;
     216                 :             :     }
     217                 :             : 
     218                 :             :     /*
     219                 :             :      * if we get here it means the index scan failed so we are at the end of
     220                 :             :      * the scan..
     221                 :             :      */
     222                 :      130876 :     return ExecClearTuple(slot);
     223                 :             : }
     224                 :             : 
     225                 :             : /*
     226                 :             :  * StoreIndexTuple
     227                 :             :  *      Fill the slot with the data the index AM returned.
     228                 :             :  *
     229                 :             :  * The data might be provided in either HeapTuple (xs_hitup) or IndexTuple
     230                 :             :  * (xs_itup) format.  Conceivably an index AM might fill both fields, in which
     231                 :             :  * case we prefer the heap format, since it's probably a bit cheaper to fill a
     232                 :             :  * slot from.
     233                 :             :  *
     234                 :             :  * At some point this might be generally-useful functionality, but
     235                 :             :  * right now we don't need it elsewhere.
     236                 :             :  */
     237                 :             : static void
     238                 :     3819563 : StoreIndexTuple(IndexOnlyScanState *node, TupleTableSlot *slot,
     239                 :             :                 IndexScanDesc scandesc)
     240                 :             : {
     241                 :     3819563 :     ExecClearTuple(slot);
     242                 :             : 
     243                 :             :     /*
     244                 :             :      * We must deform the tuple using the tupdesc the index AM formed it with
     245                 :             :      * (xs_hitupdesc or xs_itupdesc), not the slot's tupdesc.  The datums
     246                 :             :      * returned by the index AM must be binary compatible, but the descriptors
     247                 :             :      * may align each column differently in certain rare cases. (Actually,
     248                 :             :      * btree's "name" opclass stores cstring tuples that _aren't_ even binary
     249                 :             :      * compatible, in the strictest sense.  We directly handle that here.)
     250                 :             :      */
     251         [ +  + ]:     3819563 :     if (scandesc->xs_hitup)
     252                 :             :     {
     253                 :             :         Assert(slot->tts_tupleDescriptor->natts == scandesc->xs_hitupdesc->natts);
     254                 :             : 
     255                 :      969066 :         heap_deform_tuple(scandesc->xs_hitup, scandesc->xs_hitupdesc,
     256                 :             :                           slot->tts_values, slot->tts_isnull);
     257                 :             :     }
     258         [ +  - ]:     2850497 :     else if (scandesc->xs_itup)
     259                 :             :     {
     260                 :             :         Assert(slot->tts_tupleDescriptor->natts == scandesc->xs_itupdesc->natts);
     261                 :             : 
     262                 :     2850497 :         index_deform_tuple(scandesc->xs_itup, scandesc->xs_itupdesc,
     263                 :             :                            slot->tts_values, slot->tts_isnull);
     264                 :             : 
     265                 :             :         /*
     266                 :             :          * Copy all name columns stored as cstrings back into a NAMEDATALEN
     267                 :             :          * byte sized allocation.  We mark this branch as unlikely as
     268                 :             :          * generally "name" is used only for the system catalogs and this
     269                 :             :          * would have to be a user query running on those or some other user
     270                 :             :          * table with an index on a name column.
     271                 :             :          */
     272         [ +  + ]:     2850497 :         if (unlikely(node->ioss_NameCStringAttNums != NULL))
     273                 :             :         {
     274                 :        1948 :             int         attcount = node->ioss_NameCStringCount;
     275                 :             : 
     276         [ +  + ]:        3896 :             for (int idx = 0; idx < attcount; idx++)
     277                 :             :             {
     278                 :        1948 :                 int         attnum = node->ioss_NameCStringAttNums[idx];
     279                 :             :                 Name        name;
     280                 :             : 
     281                 :             :                 /* skip null Datums */
     282         [ -  + ]:        1948 :                 if (slot->tts_isnull[attnum])
     283                 :           0 :                     continue;
     284                 :             : 
     285                 :             :                 /*
     286                 :             :                  * allocate the NAMEDATALEN and copy the datum into that
     287                 :             :                  * memory
     288                 :             :                  */
     289                 :        1948 :                 name = (Name) MemoryContextAlloc(node->ss.ps.ps_ExprContext->ecxt_per_tuple_memory,
     290                 :             :                                                  NAMEDATALEN);
     291                 :             : 
     292                 :             :                 /* use namestrcpy to zero-pad all trailing bytes */
     293                 :        1948 :                 namestrcpy(name, DatumGetCString(slot->tts_values[attnum]));
     294                 :        1948 :                 slot->tts_values[attnum] = NameGetDatum(name);
     295                 :             :             }
     296                 :             :         }
     297                 :             :     }
     298                 :             :     else
     299         [ #  # ]:           0 :         elog(ERROR, "no data returned for index-only scan");
     300                 :             : 
     301                 :     3819563 :     ExecStoreVirtualTuple(slot);
     302                 :     3819563 : }
     303                 :             : 
     304                 :             : /*
     305                 :             :  * IndexOnlyRecheck -- access method routine to recheck a tuple in EvalPlanQual
     306                 :             :  *
     307                 :             :  * This can't really happen, since an index can't supply CTID which would
     308                 :             :  * be necessary data for any potential EvalPlanQual target relation.  If it
     309                 :             :  * did happen, the EPQ code would pass us the wrong data, namely a heap
     310                 :             :  * tuple not an index tuple.  So throw an error.
     311                 :             :  */
     312                 :             : static bool
     313                 :           0 : IndexOnlyRecheck(IndexOnlyScanState *node, TupleTableSlot *slot)
     314                 :             : {
     315         [ #  # ]:           0 :     elog(ERROR, "EvalPlanQual recheck is not supported in index-only scans");
     316                 :             :     return false;               /* keep compiler quiet */
     317                 :             : }
     318                 :             : 
     319                 :             : /* ----------------------------------------------------------------
     320                 :             :  *      ExecIndexOnlyScan(node)
     321                 :             :  * ----------------------------------------------------------------
     322                 :             :  */
     323                 :             : static TupleTableSlot *
     324                 :     3746520 : ExecIndexOnlyScan(PlanState *pstate)
     325                 :             : {
     326                 :     3746520 :     IndexOnlyScanState *node = castNode(IndexOnlyScanState, pstate);
     327                 :             : 
     328                 :             :     /*
     329                 :             :      * If we have runtime keys and they've not already been set up, do it now.
     330                 :             :      */
     331   [ +  +  +  + ]:     3746520 :     if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
     332                 :         372 :         ExecReScan((PlanState *) node);
     333                 :             : 
     334                 :     3746520 :     return ExecScan(&node->ss,
     335                 :             :                     (ExecScanAccessMtd) IndexOnlyNext,
     336                 :             :                     (ExecScanRecheckMtd) IndexOnlyRecheck);
     337                 :             : }
     338                 :             : 
     339                 :             : /* ----------------------------------------------------------------
     340                 :             :  *      ExecReScanIndexOnlyScan(node)
     341                 :             :  *
     342                 :             :  *      Recalculates the values of any scan keys whose value depends on
     343                 :             :  *      information known at runtime, then rescans the indexed relation.
     344                 :             :  *
     345                 :             :  *      Updating the scan key was formerly done separately in
     346                 :             :  *      ExecUpdateIndexScanKeys. Integrating it into ReScan makes
     347                 :             :  *      rescans of indices and relations/general streams more uniform.
     348                 :             :  * ----------------------------------------------------------------
     349                 :             :  */
     350                 :             : void
     351                 :      144796 : ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
     352                 :             : {
     353                 :             :     /*
     354                 :             :      * If we are doing runtime key calculations (ie, any of the index key
     355                 :             :      * values weren't simple Consts), compute the new key values.  But first,
     356                 :             :      * reset the context so we don't leak memory as each outer tuple is
     357                 :             :      * scanned.  Note this assumes that we will recalculate *all* runtime keys
     358                 :             :      * on each call.
     359                 :             :      */
     360         [ +  + ]:      144796 :     if (node->ioss_NumRuntimeKeys != 0)
     361                 :             :     {
     362                 :      144724 :         ExprContext *econtext = node->ioss_RuntimeContext;
     363                 :             : 
     364                 :      144724 :         ResetExprContext(econtext);
     365                 :      144724 :         ExecIndexEvalRuntimeKeys(econtext,
     366                 :             :                                  node->ioss_RuntimeKeys,
     367                 :             :                                  node->ioss_NumRuntimeKeys);
     368                 :             :     }
     369                 :      144796 :     node->ioss_RuntimeKeysReady = true;
     370                 :             : 
     371                 :             :     /* reset index scan */
     372         [ +  + ]:      144796 :     if (node->ioss_ScanDesc)
     373                 :      143176 :         index_rescan(node->ioss_ScanDesc,
     374                 :             :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     375                 :             :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     376                 :             : 
     377                 :      144796 :     ExecScanReScan(&node->ss);
     378                 :      144796 : }
     379                 :             : 
     380                 :             : 
     381                 :             : /* ----------------------------------------------------------------
     382                 :             :  *      ExecEndIndexOnlyScan
     383                 :             :  * ----------------------------------------------------------------
     384                 :             :  */
     385                 :             : void
     386                 :       11435 : ExecEndIndexOnlyScan(IndexOnlyScanState *node)
     387                 :             : {
     388                 :             :     Relation    indexRelationDesc;
     389                 :             :     IndexScanDesc indexScanDesc;
     390                 :             : 
     391                 :             :     /*
     392                 :             :      * extract information from the node
     393                 :             :      */
     394                 :       11435 :     indexRelationDesc = node->ioss_RelationDesc;
     395                 :       11435 :     indexScanDesc = node->ioss_ScanDesc;
     396                 :             : 
     397                 :             :     /* Release VM buffer pin, if any. */
     398         [ +  + ]:       11435 :     if (node->ioss_VMBuffer != InvalidBuffer)
     399                 :             :     {
     400                 :        4834 :         ReleaseBuffer(node->ioss_VMBuffer);
     401                 :        4834 :         node->ioss_VMBuffer = InvalidBuffer;
     402                 :             :     }
     403                 :             : 
     404                 :             :     /*
     405                 :             :      * When ending a parallel worker, copy the statistics gathered by the
     406                 :             :      * worker back into shared memory so that it can be picked up by the main
     407                 :             :      * process to report in EXPLAIN ANALYZE
     408                 :             :      */
     409   [ -  +  -  - ]:       11435 :     if (node->ioss_SharedInfo != NULL && IsParallelWorker())
     410                 :             :     {
     411                 :             :         IndexScanInstrumentation *winstrument;
     412                 :             : 
     413                 :             :         Assert(ParallelWorkerNumber < node->ioss_SharedInfo->num_workers);
     414                 :           0 :         winstrument = &node->ioss_SharedInfo->winstrument[ParallelWorkerNumber];
     415                 :             : 
     416                 :             :         /*
     417                 :             :          * We have to accumulate the stats rather than performing a memcpy.
     418                 :             :          * When a Gather/GatherMerge node finishes it will perform planner
     419                 :             :          * shutdown on the workers.  On rescan it will spin up new workers
     420                 :             :          * which will have a new IndexOnlyScanState and zeroed stats.
     421                 :             :          */
     422                 :           0 :         winstrument->nsearches += node->ioss_Instrument->nsearches;
     423                 :             :     }
     424                 :             : 
     425                 :             :     /*
     426                 :             :      * close the index relation (no-op if we didn't open it)
     427                 :             :      */
     428         [ +  + ]:       11435 :     if (indexScanDesc)
     429                 :        6889 :         index_endscan(indexScanDesc);
     430         [ +  + ]:       11435 :     if (indexRelationDesc)
     431                 :        9618 :         index_close(indexRelationDesc, NoLock);
     432                 :       11435 : }
     433                 :             : 
     434                 :             : /* ----------------------------------------------------------------
     435                 :             :  *      ExecIndexOnlyMarkPos
     436                 :             :  *
     437                 :             :  * Note: we assume that no caller attempts to set a mark before having read
     438                 :             :  * at least one tuple.  Otherwise, ioss_ScanDesc might still be NULL.
     439                 :             :  * ----------------------------------------------------------------
     440                 :             :  */
     441                 :             : void
     442                 :       82019 : ExecIndexOnlyMarkPos(IndexOnlyScanState *node)
     443                 :             : {
     444                 :       82019 :     EState     *estate = node->ss.ps.state;
     445                 :       82019 :     EPQState   *epqstate = estate->es_epq_active;
     446                 :             : 
     447         [ -  + ]:       82019 :     if (epqstate != NULL)
     448                 :             :     {
     449                 :             :         /*
     450                 :             :          * We are inside an EvalPlanQual recheck.  If a test tuple exists for
     451                 :             :          * this relation, then we shouldn't access the index at all.  We would
     452                 :             :          * instead need to save, and later restore, the state of the
     453                 :             :          * relsubs_done flag, so that re-fetching the test tuple is possible.
     454                 :             :          * However, given the assumption that no caller sets a mark at the
     455                 :             :          * start of the scan, we can only get here with relsubs_done[i]
     456                 :             :          * already set, and so no state need be saved.
     457                 :             :          */
     458                 :           0 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     459                 :             : 
     460                 :             :         Assert(scanrelid > 0);
     461         [ #  # ]:           0 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     462         [ #  # ]:           0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     463                 :             :         {
     464                 :             :             /* Verify the claim above */
     465         [ #  # ]:           0 :             if (!epqstate->relsubs_done[scanrelid - 1])
     466         [ #  # ]:           0 :                 elog(ERROR, "unexpected ExecIndexOnlyMarkPos call in EPQ recheck");
     467                 :           0 :             return;
     468                 :             :         }
     469                 :             :     }
     470                 :             : 
     471                 :       82019 :     index_markpos(node->ioss_ScanDesc);
     472                 :             : }
     473                 :             : 
     474                 :             : /* ----------------------------------------------------------------
     475                 :             :  *      ExecIndexOnlyRestrPos
     476                 :             :  * ----------------------------------------------------------------
     477                 :             :  */
     478                 :             : void
     479                 :           0 : ExecIndexOnlyRestrPos(IndexOnlyScanState *node)
     480                 :             : {
     481                 :           0 :     EState     *estate = node->ss.ps.state;
     482                 :           0 :     EPQState   *epqstate = estate->es_epq_active;
     483                 :             : 
     484         [ #  # ]:           0 :     if (estate->es_epq_active != NULL)
     485                 :             :     {
     486                 :             :         /* See comments in ExecIndexMarkPos */
     487                 :           0 :         Index       scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid;
     488                 :             : 
     489                 :             :         Assert(scanrelid > 0);
     490         [ #  # ]:           0 :         if (epqstate->relsubs_slot[scanrelid - 1] != NULL ||
     491         [ #  # ]:           0 :             epqstate->relsubs_rowmark[scanrelid - 1] != NULL)
     492                 :             :         {
     493                 :             :             /* Verify the claim above */
     494         [ #  # ]:           0 :             if (!epqstate->relsubs_done[scanrelid - 1])
     495         [ #  # ]:           0 :                 elog(ERROR, "unexpected ExecIndexOnlyRestrPos call in EPQ recheck");
     496                 :           0 :             return;
     497                 :             :         }
     498                 :             :     }
     499                 :             : 
     500                 :           0 :     index_restrpos(node->ioss_ScanDesc);
     501                 :             : }
     502                 :             : 
     503                 :             : /* ----------------------------------------------------------------
     504                 :             :  *      ExecInitIndexOnlyScan
     505                 :             :  *
     506                 :             :  *      Initializes the index scan's state information, creates
     507                 :             :  *      scan keys, and opens the base and index relations.
     508                 :             :  *
     509                 :             :  *      Note: index scans have 2 sets of state information because
     510                 :             :  *            we have to keep track of the base relation and the
     511                 :             :  *            index relation.
     512                 :             :  * ----------------------------------------------------------------
     513                 :             :  */
     514                 :             : IndexOnlyScanState *
     515                 :       11468 : ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
     516                 :             : {
     517                 :             :     IndexOnlyScanState *indexstate;
     518                 :             :     Relation    currentRelation;
     519                 :             :     Relation    indexRelation;
     520                 :             :     LOCKMODE    lockmode;
     521                 :             :     TupleDesc   tupDesc;
     522                 :             :     int         indnkeyatts;
     523                 :             :     int         namecount;
     524                 :             : 
     525                 :             :     /*
     526                 :             :      * create state structure
     527                 :             :      */
     528                 :       11468 :     indexstate = makeNode(IndexOnlyScanState);
     529                 :       11468 :     indexstate->ss.ps.plan = (Plan *) node;
     530                 :       11468 :     indexstate->ss.ps.state = estate;
     531                 :       11468 :     indexstate->ss.ps.ExecProcNode = ExecIndexOnlyScan;
     532                 :             : 
     533                 :             :     /*
     534                 :             :      * Miscellaneous initialization
     535                 :             :      *
     536                 :             :      * create expression context for node
     537                 :             :      */
     538                 :       11468 :     ExecAssignExprContext(estate, &indexstate->ss.ps);
     539                 :             : 
     540                 :             :     /*
     541                 :             :      * open the scan relation
     542                 :             :      */
     543                 :       11468 :     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
     544                 :             : 
     545                 :       11468 :     indexstate->ss.ss_currentRelation = currentRelation;
     546                 :       11468 :     indexstate->ss.ss_currentScanDesc = NULL;    /* no heap scan here */
     547                 :             : 
     548                 :             :     /*
     549                 :             :      * Build the scan tuple type using the indextlist generated by the
     550                 :             :      * planner.  We use this, rather than the index's physical tuple
     551                 :             :      * descriptor, because the latter contains storage column types not the
     552                 :             :      * types of the original datums.  (It's the AM's responsibility to return
     553                 :             :      * suitable data anyway.)
     554                 :             :      */
     555                 :       11468 :     tupDesc = ExecTypeFromTL(node->indextlist);
     556                 :       11468 :     ExecInitScanTupleSlot(estate, &indexstate->ss, tupDesc,
     557                 :             :                           &TTSOpsVirtual,
     558                 :             :                           0);
     559                 :             : 
     560                 :             :     /*
     561                 :             :      * We need another slot, in a format that's suitable for the table AM, for
     562                 :             :      * when we need to fetch a tuple from the table for rechecking visibility.
     563                 :             :      */
     564                 :       11468 :     indexstate->ioss_TableSlot =
     565                 :       11468 :         ExecAllocTableSlot(&estate->es_tupleTable,
     566                 :             :                            RelationGetDescr(currentRelation),
     567                 :             :                            table_slot_callbacks(currentRelation), 0);
     568                 :             : 
     569                 :             :     /*
     570                 :             :      * Initialize result type and projection info.  The node's targetlist will
     571                 :             :      * contain Vars with varno = INDEX_VAR, referencing the scan tuple.
     572                 :             :      */
     573                 :       11468 :     ExecInitResultTypeTL(&indexstate->ss.ps);
     574                 :       11468 :     ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
     575                 :             : 
     576                 :             :     /*
     577                 :             :      * initialize child expressions
     578                 :             :      *
     579                 :             :      * Note: we don't initialize all of the indexorderby expression, only the
     580                 :             :      * sub-parts corresponding to runtime keys (see below).
     581                 :             :      */
     582                 :       11468 :     indexstate->ss.ps.qual =
     583                 :       11468 :         ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
     584                 :       11468 :     indexstate->recheckqual =
     585                 :       11468 :         ExecInitQual(node->recheckqual, (PlanState *) indexstate);
     586                 :             : 
     587                 :             :     /*
     588                 :             :      * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
     589                 :             :      * here.  This allows an index-advisor plugin to EXPLAIN a plan containing
     590                 :             :      * references to nonexistent indexes.
     591                 :             :      */
     592         [ +  + ]:       11468 :     if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
     593                 :        1817 :         return indexstate;
     594                 :             : 
     595                 :             :     /* Set up instrumentation of index-only scans if requested */
     596         [ +  + ]:        9651 :     if (estate->es_instrument)
     597                 :          80 :         indexstate->ioss_Instrument = palloc0_object(IndexScanInstrumentation);
     598                 :             : 
     599                 :             :     /* Open the index relation. */
     600                 :        9651 :     lockmode = exec_rt_fetch(node->scan.scanrelid, estate)->rellockmode;
     601                 :        9651 :     indexRelation = index_open(node->indexid, lockmode);
     602                 :        9651 :     indexstate->ioss_RelationDesc = indexRelation;
     603                 :             : 
     604                 :             :     /*
     605                 :             :      * Initialize index-specific scan state
     606                 :             :      */
     607                 :        9651 :     indexstate->ioss_RuntimeKeysReady = false;
     608                 :        9651 :     indexstate->ioss_RuntimeKeys = NULL;
     609                 :        9651 :     indexstate->ioss_NumRuntimeKeys = 0;
     610                 :             : 
     611                 :             :     /*
     612                 :             :      * build the index scan keys from the index qualification
     613                 :             :      */
     614                 :        9651 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     615                 :             :                            indexRelation,
     616                 :             :                            node->indexqual,
     617                 :             :                            false,
     618                 :        9651 :                            &indexstate->ioss_ScanKeys,
     619                 :             :                            &indexstate->ioss_NumScanKeys,
     620                 :             :                            &indexstate->ioss_RuntimeKeys,
     621                 :             :                            &indexstate->ioss_NumRuntimeKeys,
     622                 :             :                            NULL,    /* no ArrayKeys */
     623                 :             :                            NULL);
     624                 :             : 
     625                 :             :     /*
     626                 :             :      * any ORDER BY exprs have to be turned into scankeys in the same way
     627                 :             :      */
     628                 :        9651 :     ExecIndexBuildScanKeys((PlanState *) indexstate,
     629                 :             :                            indexRelation,
     630                 :             :                            node->indexorderby,
     631                 :             :                            true,
     632                 :        9651 :                            &indexstate->ioss_OrderByKeys,
     633                 :             :                            &indexstate->ioss_NumOrderByKeys,
     634                 :             :                            &indexstate->ioss_RuntimeKeys,
     635                 :             :                            &indexstate->ioss_NumRuntimeKeys,
     636                 :             :                            NULL,    /* no ArrayKeys */
     637                 :             :                            NULL);
     638                 :             : 
     639                 :             :     /*
     640                 :             :      * If we have runtime keys, we need an ExprContext to evaluate them. The
     641                 :             :      * node's standard context won't do because we want to reset that context
     642                 :             :      * for every tuple.  So, build another context just like the other one...
     643                 :             :      * -tgl 7/11/00
     644                 :             :      */
     645         [ +  + ]:        9651 :     if (indexstate->ioss_NumRuntimeKeys != 0)
     646                 :             :     {
     647                 :        4218 :         ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
     648                 :             : 
     649                 :        4218 :         ExecAssignExprContext(estate, &indexstate->ss.ps);
     650                 :        4218 :         indexstate->ioss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
     651                 :        4218 :         indexstate->ss.ps.ps_ExprContext = stdecontext;
     652                 :             :     }
     653                 :             :     else
     654                 :             :     {
     655                 :        5433 :         indexstate->ioss_RuntimeContext = NULL;
     656                 :             :     }
     657                 :             : 
     658                 :        9651 :     indexstate->ioss_NameCStringAttNums = NULL;
     659                 :        9651 :     indnkeyatts = indexRelation->rd_index->indnkeyatts;
     660                 :        9651 :     namecount = 0;
     661                 :             : 
     662                 :             :     /*
     663                 :             :      * The "name" type for btree uses text_ops which results in storing
     664                 :             :      * cstrings in the indexed keys rather than names.  Here we detect that in
     665                 :             :      * a generic way in case other index AMs want to do the same optimization.
     666                 :             :      * Check for opclasses with an opcintype of NAMEOID and an index tuple
     667                 :             :      * descriptor with CSTRINGOID.  If any of these are found, create an array
     668                 :             :      * marking the index attribute number of each of them.  StoreIndexTuple()
     669                 :             :      * handles copying the name Datums into a NAMEDATALEN-byte allocation.
     670                 :             :      */
     671                 :             : 
     672                 :             :     /* First, count the number of such index keys */
     673         [ +  + ]:       23989 :     for (int attnum = 0; attnum < indnkeyatts; attnum++)
     674                 :             :     {
     675         [ +  + ]:       14338 :         if (TupleDescAttr(indexRelation->rd_att, attnum)->atttypid == CSTRINGOID &&
     676         [ +  - ]:        1763 :             indexRelation->rd_opcintype[attnum] == NAMEOID)
     677                 :        1763 :             namecount++;
     678                 :             :     }
     679                 :             : 
     680         [ +  + ]:        9651 :     if (namecount > 0)
     681                 :             :     {
     682                 :        1763 :         int         idx = 0;
     683                 :             : 
     684                 :             :         /*
     685                 :             :          * Now create an array to mark the attribute numbers of the keys that
     686                 :             :          * need to be converted from cstring to name.
     687                 :             :          */
     688                 :        1763 :         indexstate->ioss_NameCStringAttNums = palloc_array(AttrNumber, namecount);
     689                 :             : 
     690         [ +  + ]:        5348 :         for (int attnum = 0; attnum < indnkeyatts; attnum++)
     691                 :             :         {
     692         [ +  + ]:        3585 :             if (TupleDescAttr(indexRelation->rd_att, attnum)->atttypid == CSTRINGOID &&
     693         [ +  - ]:        1763 :                 indexRelation->rd_opcintype[attnum] == NAMEOID)
     694                 :        1763 :                 indexstate->ioss_NameCStringAttNums[idx++] = (AttrNumber) attnum;
     695                 :             :         }
     696                 :             :     }
     697                 :             : 
     698                 :        9651 :     indexstate->ioss_NameCStringCount = namecount;
     699                 :             : 
     700                 :             :     /*
     701                 :             :      * all done.
     702                 :             :      */
     703                 :        9651 :     return indexstate;
     704                 :             : }
     705                 :             : 
     706                 :             : /* ----------------------------------------------------------------
     707                 :             :  *      Parallel Index-only Scan Support
     708                 :             :  * ----------------------------------------------------------------
     709                 :             :  */
     710                 :             : 
     711                 :             : /* ----------------------------------------------------------------
     712                 :             :  *      ExecIndexOnlyScanEstimate
     713                 :             :  *
     714                 :             :  *      Compute the amount of space we'll need in the parallel
     715                 :             :  *      query DSM, and inform pcxt->estimator about our needs.
     716                 :             :  * ----------------------------------------------------------------
     717                 :             :  */
     718                 :             : void
     719                 :          30 : ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
     720                 :             :                           ParallelContext *pcxt)
     721                 :             : {
     722                 :          30 :     EState     *estate = node->ss.ps.state;
     723                 :             : 
     724                 :          30 :     node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
     725                 :             :                                                       node->ioss_NumScanKeys,
     726                 :             :                                                       node->ioss_NumOrderByKeys,
     727                 :             :                                                       estate->es_snapshot);
     728                 :          30 :     shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
     729                 :          30 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     730                 :          30 : }
     731                 :             : 
     732                 :             : /* ----------------------------------------------------------------
     733                 :             :  *      ExecIndexOnlyScanInitializeDSM
     734                 :             :  *
     735                 :             :  *      Set up a parallel index-only scan descriptor.
     736                 :             :  * ----------------------------------------------------------------
     737                 :             :  */
     738                 :             : void
     739                 :          30 : ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
     740                 :             :                                ParallelContext *pcxt)
     741                 :             : {
     742                 :          30 :     EState     *estate = node->ss.ps.state;
     743                 :             :     ParallelIndexScanDesc piscan;
     744                 :             : 
     745                 :          30 :     piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
     746                 :          30 :     index_parallelscan_initialize(node->ss.ss_currentRelation,
     747                 :             :                                   node->ioss_RelationDesc,
     748                 :             :                                   estate->es_snapshot,
     749                 :             :                                   piscan);
     750                 :          30 :     shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
     751                 :             : 
     752                 :          30 :     node->ioss_ScanDesc =
     753         [ +  - ]:          30 :         index_beginscan_parallel(node->ss.ss_currentRelation,
     754                 :             :                                  node->ioss_RelationDesc,
     755                 :             :                                  node->ioss_Instrument,
     756                 :             :                                  node->ioss_NumScanKeys,
     757                 :             :                                  node->ioss_NumOrderByKeys,
     758                 :             :                                  piscan,
     759                 :          30 :                                  ScanRelIsReadOnly(&node->ss) ?
     760                 :             :                                  SO_HINT_REL_READ_ONLY : SO_NONE);
     761                 :          30 :     node->ioss_ScanDesc->xs_want_itup = true;
     762                 :          30 :     node->ioss_VMBuffer = InvalidBuffer;
     763                 :             : 
     764                 :             :     /*
     765                 :             :      * If no run-time keys to calculate or they are ready, go ahead and pass
     766                 :             :      * the scankeys to the index AM.
     767                 :             :      */
     768   [ -  +  -  - ]:          30 :     if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     769                 :          30 :         index_rescan(node->ioss_ScanDesc,
     770                 :             :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     771                 :             :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     772                 :          30 : }
     773                 :             : 
     774                 :             : /* ----------------------------------------------------------------
     775                 :             :  *      ExecIndexOnlyScanReInitializeDSM
     776                 :             :  *
     777                 :             :  *      Reset shared state before beginning a fresh scan.
     778                 :             :  * ----------------------------------------------------------------
     779                 :             :  */
     780                 :             : void
     781                 :           8 : ExecIndexOnlyScanReInitializeDSM(IndexOnlyScanState *node,
     782                 :             :                                  ParallelContext *pcxt)
     783                 :             : {
     784                 :             :     Assert(node->ss.ps.plan->parallel_aware);
     785                 :           8 :     index_parallelrescan(node->ioss_ScanDesc);
     786                 :           8 : }
     787                 :             : 
     788                 :             : /* ----------------------------------------------------------------
     789                 :             :  *      ExecIndexOnlyScanInitializeWorker
     790                 :             :  *
     791                 :             :  *      Copy relevant information from TOC into planstate.
     792                 :             :  * ----------------------------------------------------------------
     793                 :             :  */
     794                 :             : void
     795                 :         136 : ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
     796                 :             :                                   ParallelWorkerContext *pwcxt)
     797                 :             : {
     798                 :             :     ParallelIndexScanDesc piscan;
     799                 :             : 
     800                 :         136 :     piscan = shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, false);
     801                 :             : 
     802                 :         136 :     node->ioss_ScanDesc =
     803         [ +  - ]:         136 :         index_beginscan_parallel(node->ss.ss_currentRelation,
     804                 :             :                                  node->ioss_RelationDesc,
     805                 :             :                                  node->ioss_Instrument,
     806                 :             :                                  node->ioss_NumScanKeys,
     807                 :             :                                  node->ioss_NumOrderByKeys,
     808                 :             :                                  piscan,
     809                 :         136 :                                  ScanRelIsReadOnly(&node->ss) ?
     810                 :             :                                  SO_HINT_REL_READ_ONLY : SO_NONE);
     811                 :         136 :     node->ioss_ScanDesc->xs_want_itup = true;
     812                 :             : 
     813                 :             :     /*
     814                 :             :      * If no run-time keys to calculate or they are ready, go ahead and pass
     815                 :             :      * the scankeys to the index AM.
     816                 :             :      */
     817   [ -  +  -  - ]:         136 :     if (node->ioss_NumRuntimeKeys == 0 || node->ioss_RuntimeKeysReady)
     818                 :         136 :         index_rescan(node->ioss_ScanDesc,
     819                 :             :                      node->ioss_ScanKeys, node->ioss_NumScanKeys,
     820                 :             :                      node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
     821                 :         136 : }
     822                 :             : 
     823                 :             : /*
     824                 :             :  * Compute the amount of space we'll need for the shared instrumentation and
     825                 :             :  * inform pcxt->estimator.
     826                 :             :  */
     827                 :             : void
     828                 :          38 : ExecIndexOnlyScanInstrumentEstimate(IndexOnlyScanState *node,
     829                 :             :                                     ParallelContext *pcxt)
     830                 :             : {
     831                 :             :     Size        size;
     832                 :             : 
     833   [ -  +  -  - ]:          38 :     if (!node->ss.ps.instrument || pcxt->nworkers == 0)
     834                 :          38 :         return;
     835                 :             : 
     836                 :             :     /*
     837                 :             :      * This size calculation is trivial enough that we don't bother saving it
     838                 :             :      * in the IndexOnlyScanState. We'll recalculate the needed size in
     839                 :             :      * ExecIndexOnlyScanInstrumentInitDSM().
     840                 :             :      */
     841                 :           0 :     size = add_size(offsetof(SharedIndexScanInstrumentation, winstrument),
     842                 :           0 :                     mul_size(pcxt->nworkers, sizeof(IndexScanInstrumentation)));
     843                 :           0 :     shm_toc_estimate_chunk(&pcxt->estimator, size);
     844                 :           0 :     shm_toc_estimate_keys(&pcxt->estimator, 1);
     845                 :             : }
     846                 :             : 
     847                 :             : /*
     848                 :             :  * Set up parallel index-only scan instrumentation.
     849                 :             :  */
     850                 :             : void
     851                 :          38 : ExecIndexOnlyScanInstrumentInitDSM(IndexOnlyScanState *node,
     852                 :             :                                    ParallelContext *pcxt)
     853                 :             : {
     854                 :             :     Size        size;
     855                 :             : 
     856   [ -  +  -  - ]:          38 :     if (!node->ss.ps.instrument || pcxt->nworkers == 0)
     857                 :          38 :         return;
     858                 :             : 
     859                 :           0 :     size = add_size(offsetof(SharedIndexScanInstrumentation, winstrument),
     860                 :           0 :                     mul_size(pcxt->nworkers, sizeof(IndexScanInstrumentation)));
     861                 :           0 :     node->ioss_SharedInfo =
     862                 :           0 :         (SharedIndexScanInstrumentation *) shm_toc_allocate(pcxt->toc, size);
     863                 :             : 
     864                 :             :     /* Each per-worker area must start out as zeroes */
     865                 :           0 :     memset(node->ioss_SharedInfo, 0, size);
     866                 :           0 :     node->ioss_SharedInfo->num_workers = pcxt->nworkers;
     867                 :           0 :     shm_toc_insert(pcxt->toc,
     868                 :           0 :                    node->ss.ps.plan->plan_node_id +
     869                 :             :                    PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
     870                 :           0 :                    node->ioss_SharedInfo);
     871                 :             : }
     872                 :             : 
     873                 :             : /*
     874                 :             :  * Look up and save the location of the shared instrumentation.
     875                 :             :  */
     876                 :             : void
     877                 :         160 : ExecIndexOnlyScanInstrumentInitWorker(IndexOnlyScanState *node,
     878                 :             :                                       ParallelWorkerContext *pwcxt)
     879                 :             : {
     880         [ +  - ]:         160 :     if (!node->ss.ps.instrument)
     881                 :         160 :         return;
     882                 :             : 
     883                 :           0 :     node->ioss_SharedInfo = (SharedIndexScanInstrumentation *)
     884                 :           0 :         shm_toc_lookup(pwcxt->toc,
     885                 :           0 :                        node->ss.ps.plan->plan_node_id +
     886                 :             :                        PARALLEL_KEY_SCAN_INSTRUMENT_OFFSET,
     887                 :             :                        false);
     888                 :             : }
     889                 :             : 
     890                 :             : /* ----------------------------------------------------------------
     891                 :             :  *      ExecIndexOnlyScanRetrieveInstrumentation
     892                 :             :  *
     893                 :             :  *      Transfer index-only scan statistics from DSM to private memory.
     894                 :             :  * ----------------------------------------------------------------
     895                 :             :  */
     896                 :             : void
     897                 :           0 : ExecIndexOnlyScanRetrieveInstrumentation(IndexOnlyScanState *node)
     898                 :             : {
     899                 :           0 :     SharedIndexScanInstrumentation *SharedInfo = node->ioss_SharedInfo;
     900                 :             :     size_t      size;
     901                 :             : 
     902         [ #  # ]:           0 :     if (SharedInfo == NULL)
     903                 :           0 :         return;
     904                 :             : 
     905                 :             :     /* Create a copy of SharedInfo in backend-local memory */
     906                 :           0 :     size = offsetof(SharedIndexScanInstrumentation, winstrument) +
     907                 :           0 :         SharedInfo->num_workers * sizeof(IndexScanInstrumentation);
     908                 :           0 :     node->ioss_SharedInfo = palloc(size);
     909                 :           0 :     memcpy(node->ioss_SharedInfo, SharedInfo, size);
     910                 :             : }
        

Generated by: LCOV version 2.0-1