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

Generated by: LCOV version 2.0-1