LCOV - code coverage report
Current view: top level - src/backend/access/index - indexam.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 97.4 % 231 225
Test Date: 2026-04-07 07:16:34 Functions: 100.0 % 28 28
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * indexam.c
       4              :  *    general index access method routines
       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/access/index/indexam.c
      12              :  *
      13              :  * INTERFACE ROUTINES
      14              :  *      index_open      - open an index relation by relation OID
      15              :  *      index_close     - close an index relation
      16              :  *      index_beginscan - start a scan of an index with amgettuple
      17              :  *      index_beginscan_bitmap - start a scan of an index with amgetbitmap
      18              :  *      index_rescan    - restart a scan of an index
      19              :  *      index_endscan   - end a scan
      20              :  *      index_insert    - insert an index tuple into a relation
      21              :  *      index_markpos   - mark a scan position
      22              :  *      index_restrpos  - restore a scan position
      23              :  *      index_parallelscan_estimate - estimate shared memory for parallel scan
      24              :  *      index_parallelscan_initialize - initialize parallel scan
      25              :  *      index_parallelrescan  - (re)start a parallel scan of an index
      26              :  *      index_beginscan_parallel - join parallel index scan
      27              :  *      index_getnext_tid   - get the next TID from a scan
      28              :  *      index_fetch_heap        - get the scan's next heap tuple
      29              :  *      index_getnext_slot  - get the next tuple from a scan
      30              :  *      index_getbitmap - get all tuples from a scan
      31              :  *      index_bulk_delete   - bulk deletion of index tuples
      32              :  *      index_vacuum_cleanup    - post-deletion cleanup of an index
      33              :  *      index_can_return    - does index support index-only scans?
      34              :  *      index_getprocid - get a support procedure OID
      35              :  *      index_getprocinfo - get a support procedure's lookup info
      36              :  *
      37              :  * NOTES
      38              :  *      This file contains the index_ routines which used
      39              :  *      to be a scattered collection of stuff in access/genam.
      40              :  *
      41              :  *-------------------------------------------------------------------------
      42              :  */
      43              : 
      44              : #include "postgres.h"
      45              : 
      46              : #include "access/amapi.h"
      47              : #include "access/relation.h"
      48              : #include "access/reloptions.h"
      49              : #include "access/relscan.h"
      50              : #include "access/tableam.h"
      51              : #include "catalog/index.h"
      52              : #include "catalog/pg_type.h"
      53              : #include "nodes/execnodes.h"
      54              : #include "pgstat.h"
      55              : #include "storage/lmgr.h"
      56              : #include "storage/lock.h"
      57              : #include "storage/predicate.h"
      58              : #include "utils/ruleutils.h"
      59              : #include "utils/snapmgr.h"
      60              : #include "utils/syscache.h"
      61              : 
      62              : 
      63              : /* ----------------------------------------------------------------
      64              :  *                  macros used in index_ routines
      65              :  *
      66              :  * Note: the ReindexIsProcessingIndex() check in RELATION_CHECKS is there
      67              :  * to check that we don't try to scan or do retail insertions into an index
      68              :  * that is currently being rebuilt or pending rebuild.  This helps to catch
      69              :  * things that don't work when reindexing system catalogs, as well as prevent
      70              :  * user errors like index expressions that access their own tables.  The check
      71              :  * doesn't prevent the actual rebuild because we don't use RELATION_CHECKS
      72              :  * when calling the index AM's ambuild routine, and there is no reason for
      73              :  * ambuild to call its subsidiary routines through this file.
      74              :  * ----------------------------------------------------------------
      75              :  */
      76              : #define RELATION_CHECKS \
      77              : do { \
      78              :     Assert(RelationIsValid(indexRelation)); \
      79              :     Assert(indexRelation->rd_indam); \
      80              :     if (unlikely(ReindexIsProcessingIndex(RelationGetRelid(indexRelation)))) \
      81              :         ereport(ERROR, \
      82              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
      83              :                  errmsg("cannot access index \"%s\" while it is being reindexed", \
      84              :                         RelationGetRelationName(indexRelation)))); \
      85              : } while(0)
      86              : 
      87              : #define SCAN_CHECKS \
      88              : ( \
      89              :     AssertMacro(scan), \
      90              :     AssertMacro(RelationIsValid(scan->indexRelation)), \
      91              :     AssertMacro(scan->indexRelation->rd_indam) \
      92              : )
      93              : 
      94              : #define CHECK_REL_PROCEDURE(pname) \
      95              : do { \
      96              :     if (indexRelation->rd_indam->pname == NULL) \
      97              :         elog(ERROR, "function \"%s\" is not defined for index \"%s\"", \
      98              :              CppAsString(pname), RelationGetRelationName(indexRelation)); \
      99              : } while(0)
     100              : 
     101              : #define CHECK_SCAN_PROCEDURE(pname) \
     102              : do { \
     103              :     if (scan->indexRelation->rd_indam->pname == NULL) \
     104              :         elog(ERROR, "function \"%s\" is not defined for index \"%s\"", \
     105              :              CppAsString(pname), RelationGetRelationName(scan->indexRelation)); \
     106              : } while(0)
     107              : 
     108              : static IndexScanDesc index_beginscan_internal(Relation indexRelation,
     109              :                                               int nkeys, int norderbys, Snapshot snapshot,
     110              :                                               ParallelIndexScanDesc pscan, bool temp_snap);
     111              : static inline void validate_relation_as_index(Relation r);
     112              : 
     113              : 
     114              : /* ----------------------------------------------------------------
     115              :  *                 index_ interface functions
     116              :  * ----------------------------------------------------------------
     117              :  */
     118              : 
     119              : /* ----------------
     120              :  *      index_open - open an index relation by relation OID
     121              :  *
     122              :  *      If lockmode is not "NoLock", the specified kind of lock is
     123              :  *      obtained on the index.  (Generally, NoLock should only be
     124              :  *      used if the caller knows it has some appropriate lock on the
     125              :  *      index already.)
     126              :  *
     127              :  *      An error is raised if the index does not exist.
     128              :  *
     129              :  *      This is a convenience routine adapted for indexscan use.
     130              :  *      Some callers may prefer to use relation_open directly.
     131              :  * ----------------
     132              :  */
     133              : Relation
     134     13200012 : index_open(Oid relationId, LOCKMODE lockmode)
     135              : {
     136              :     Relation    r;
     137              : 
     138     13200012 :     r = relation_open(relationId, lockmode);
     139              : 
     140     13200007 :     validate_relation_as_index(r);
     141              : 
     142     13199993 :     return r;
     143              : }
     144              : 
     145              : /* ----------------
     146              :  *      try_index_open - open an index relation by relation OID
     147              :  *
     148              :  *      Same as index_open, except return NULL instead of failing
     149              :  *      if the relation does not exist.
     150              :  * ----------------
     151              :  */
     152              : Relation
     153         1175 : try_index_open(Oid relationId, LOCKMODE lockmode)
     154              : {
     155              :     Relation    r;
     156              : 
     157         1175 :     r = try_relation_open(relationId, lockmode);
     158              : 
     159              :     /* leave if index does not exist */
     160         1175 :     if (!r)
     161            0 :         return NULL;
     162              : 
     163         1175 :     validate_relation_as_index(r);
     164              : 
     165         1175 :     return r;
     166              : }
     167              : 
     168              : /* ----------------
     169              :  *      index_close - close an index relation
     170              :  *
     171              :  *      If lockmode is not "NoLock", we then release the specified lock.
     172              :  *
     173              :  *      Note that it is often sensible to hold a lock beyond index_close;
     174              :  *      in that case, the lock is released automatically at xact end.
     175              :  * ----------------
     176              :  */
     177              : void
     178     13228539 : index_close(Relation relation, LOCKMODE lockmode)
     179              : {
     180     13228539 :     LockRelId   relid = relation->rd_lockInfo.lockRelId;
     181              : 
     182              :     Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
     183              : 
     184              :     /* The relcache does the real work... */
     185     13228539 :     RelationClose(relation);
     186              : 
     187     13228539 :     if (lockmode != NoLock)
     188     12047878 :         UnlockRelationId(&relid, lockmode);
     189     13228539 : }
     190              : 
     191              : /* ----------------
     192              :  *      validate_relation_as_index
     193              :  *
     194              :  *      Make sure relkind is an index or a partitioned index.
     195              :  * ----------------
     196              :  */
     197              : static inline void
     198     13201182 : validate_relation_as_index(Relation r)
     199              : {
     200     13201182 :     if (r->rd_rel->relkind != RELKIND_INDEX &&
     201         8579 :         r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
     202           14 :         ereport(ERROR,
     203              :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
     204              :                  errmsg("\"%s\" is not an index",
     205              :                         RelationGetRelationName(r))));
     206     13201168 : }
     207              : 
     208              : 
     209              : /* ----------------
     210              :  *      index_insert - insert an index tuple into a relation
     211              :  * ----------------
     212              :  */
     213              : bool
     214      5900059 : index_insert(Relation indexRelation,
     215              :              Datum *values,
     216              :              bool *isnull,
     217              :              ItemPointer heap_t_ctid,
     218              :              Relation heapRelation,
     219              :              IndexUniqueCheck checkUnique,
     220              :              bool indexUnchanged,
     221              :              IndexInfo *indexInfo)
     222              : {
     223      5900059 :     RELATION_CHECKS;
     224      5900059 :     CHECK_REL_PROCEDURE(aminsert);
     225              : 
     226      5900059 :     if (!(indexRelation->rd_indam->ampredlocks))
     227       333627 :         CheckForSerializableConflictIn(indexRelation,
     228              :                                        (ItemPointer) NULL,
     229              :                                        InvalidBlockNumber);
     230              : 
     231      5900059 :     return indexRelation->rd_indam->aminsert(indexRelation, values, isnull,
     232              :                                              heap_t_ctid, heapRelation,
     233              :                                              checkUnique, indexUnchanged,
     234              :                                              indexInfo);
     235              : }
     236              : 
     237              : /* -------------------------
     238              :  *      index_insert_cleanup - clean up after all index inserts are done
     239              :  * -------------------------
     240              :  */
     241              : void
     242      2275403 : index_insert_cleanup(Relation indexRelation,
     243              :                      IndexInfo *indexInfo)
     244              : {
     245      2275403 :     RELATION_CHECKS;
     246              : 
     247      2275403 :     if (indexRelation->rd_indam->aminsertcleanup)
     248          740 :         indexRelation->rd_indam->aminsertcleanup(indexRelation, indexInfo);
     249      2275403 : }
     250              : 
     251              : /*
     252              :  * index_beginscan - start a scan of an index with amgettuple
     253              :  *
     254              :  * Caller must be holding suitable locks on the heap and the index.
     255              :  */
     256              : IndexScanDesc
     257      9836706 : index_beginscan(Relation heapRelation,
     258              :                 Relation indexRelation,
     259              :                 Snapshot snapshot,
     260              :                 IndexScanInstrumentation *instrument,
     261              :                 int nkeys, int norderbys,
     262              :                 uint32 flags)
     263              : {
     264              :     IndexScanDesc scan;
     265              : 
     266              :     Assert(snapshot != InvalidSnapshot);
     267              : 
     268              :     /* Check that a historic snapshot is not used for non-catalog tables */
     269      9836706 :     if (IsHistoricMVCCSnapshot(snapshot) &&
     270        17349 :         !RelationIsAccessibleInLogicalDecoding(heapRelation))
     271              :     {
     272            0 :         ereport(ERROR,
     273              :                 (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
     274              :                  errmsg("cannot query non-catalog table \"%s\" during logical decoding",
     275              :                         RelationGetRelationName(heapRelation))));
     276              :     }
     277              : 
     278      9836706 :     scan = index_beginscan_internal(indexRelation, nkeys, norderbys, snapshot, NULL, false);
     279              : 
     280              :     /*
     281              :      * Save additional parameters into the scandesc.  Everything else was set
     282              :      * up by RelationGetIndexScan.
     283              :      */
     284      9836706 :     scan->heapRelation = heapRelation;
     285      9836706 :     scan->xs_snapshot = snapshot;
     286      9836706 :     scan->instrument = instrument;
     287              : 
     288              :     /* prepare to fetch index matches from table */
     289      9836706 :     scan->xs_heapfetch = table_index_fetch_begin(heapRelation, flags);
     290              : 
     291      9836706 :     return scan;
     292              : }
     293              : 
     294              : /*
     295              :  * index_beginscan_bitmap - start a scan of an index with amgetbitmap
     296              :  *
     297              :  * As above, caller had better be holding some lock on the parent heap
     298              :  * relation, even though it's not explicitly mentioned here.
     299              :  */
     300              : IndexScanDesc
     301        14545 : index_beginscan_bitmap(Relation indexRelation,
     302              :                        Snapshot snapshot,
     303              :                        IndexScanInstrumentation *instrument,
     304              :                        int nkeys)
     305              : {
     306              :     IndexScanDesc scan;
     307              : 
     308              :     Assert(snapshot != InvalidSnapshot);
     309              : 
     310        14545 :     scan = index_beginscan_internal(indexRelation, nkeys, 0, snapshot, NULL, false);
     311              : 
     312              :     /*
     313              :      * Save additional parameters into the scandesc.  Everything else was set
     314              :      * up by RelationGetIndexScan.
     315              :      */
     316        14545 :     scan->xs_snapshot = snapshot;
     317        14545 :     scan->instrument = instrument;
     318              : 
     319        14545 :     return scan;
     320              : }
     321              : 
     322              : /*
     323              :  * index_beginscan_internal --- common code for index_beginscan variants
     324              :  */
     325              : static IndexScanDesc
     326      9851509 : index_beginscan_internal(Relation indexRelation,
     327              :                          int nkeys, int norderbys, Snapshot snapshot,
     328              :                          ParallelIndexScanDesc pscan, bool temp_snap)
     329              : {
     330              :     IndexScanDesc scan;
     331              : 
     332      9851509 :     RELATION_CHECKS;
     333      9851509 :     CHECK_REL_PROCEDURE(ambeginscan);
     334              : 
     335      9851509 :     if (!(indexRelation->rd_indam->ampredlocks))
     336         2953 :         PredicateLockRelation(indexRelation, snapshot);
     337              : 
     338              :     /*
     339              :      * We hold a reference count to the relcache entry throughout the scan.
     340              :      */
     341      9851509 :     RelationIncrementReferenceCount(indexRelation);
     342              : 
     343              :     /*
     344              :      * Tell the AM to open a scan.
     345              :      */
     346      9851509 :     scan = indexRelation->rd_indam->ambeginscan(indexRelation, nkeys,
     347              :                                                 norderbys);
     348              :     /* Initialize information for parallel scan. */
     349      9851509 :     scan->parallel_scan = pscan;
     350      9851509 :     scan->xs_temp_snap = temp_snap;
     351              : 
     352      9851509 :     return scan;
     353              : }
     354              : 
     355              : /* ----------------
     356              :  *      index_rescan  - (re)start a scan of an index
     357              :  *
     358              :  * During a restart, the caller may specify a new set of scankeys and/or
     359              :  * orderbykeys; but the number of keys cannot differ from what index_beginscan
     360              :  * was told.  (Later we might relax that to "must not exceed", but currently
     361              :  * the index AMs tend to assume that scan->numberOfKeys is what to believe.)
     362              :  * To restart the scan without changing keys, pass NULL for the key arrays.
     363              :  * (Of course, keys *must* be passed on the first call, unless
     364              :  * scan->numberOfKeys is zero.)
     365              :  * ----------------
     366              :  */
     367              : void
     368     10320399 : index_rescan(IndexScanDesc scan,
     369              :              ScanKey keys, int nkeys,
     370              :              ScanKey orderbys, int norderbys)
     371              : {
     372              :     SCAN_CHECKS;
     373     10320399 :     CHECK_SCAN_PROCEDURE(amrescan);
     374              : 
     375              :     Assert(nkeys == scan->numberOfKeys);
     376              :     Assert(norderbys == scan->numberOfOrderBys);
     377              : 
     378              :     /* reset table AM state for rescan */
     379     10320399 :     if (scan->xs_heapfetch)
     380     10303522 :         table_index_fetch_reset(scan->xs_heapfetch);
     381              : 
     382     10320399 :     scan->kill_prior_tuple = false; /* for safety */
     383     10320399 :     scan->xs_heap_continue = false;
     384              : 
     385     10320399 :     scan->indexRelation->rd_indam->amrescan(scan, keys, nkeys,
     386              :                                             orderbys, norderbys);
     387     10320399 : }
     388              : 
     389              : /* ----------------
     390              :  *      index_endscan - end a scan
     391              :  * ----------------
     392              :  */
     393              : void
     394      9850223 : index_endscan(IndexScanDesc scan)
     395              : {
     396              :     SCAN_CHECKS;
     397      9850223 :     CHECK_SCAN_PROCEDURE(amendscan);
     398              : 
     399              :     /* Release resources (like buffer pins) from table accesses */
     400      9850223 :     if (scan->xs_heapfetch)
     401              :     {
     402      9835759 :         table_index_fetch_end(scan->xs_heapfetch);
     403      9835759 :         scan->xs_heapfetch = NULL;
     404              :     }
     405              : 
     406              :     /* End the AM's scan */
     407      9850223 :     scan->indexRelation->rd_indam->amendscan(scan);
     408              : 
     409              :     /* Release index refcount acquired by index_beginscan */
     410      9850223 :     RelationDecrementReferenceCount(scan->indexRelation);
     411              : 
     412      9850223 :     if (scan->xs_temp_snap)
     413          258 :         UnregisterSnapshot(scan->xs_snapshot);
     414              : 
     415              :     /* Release the scan data structure itself */
     416      9850223 :     IndexScanEnd(scan);
     417      9850223 : }
     418              : 
     419              : /* ----------------
     420              :  *      index_markpos  - mark a scan position
     421              :  * ----------------
     422              :  */
     423              : void
     424        86084 : index_markpos(IndexScanDesc scan)
     425              : {
     426              :     SCAN_CHECKS;
     427        86084 :     CHECK_SCAN_PROCEDURE(ammarkpos);
     428              : 
     429        86084 :     scan->indexRelation->rd_indam->ammarkpos(scan);
     430        86084 : }
     431              : 
     432              : /* ----------------
     433              :  *      index_restrpos  - restore a scan position
     434              :  *
     435              :  * NOTE: this only restores the internal scan state of the index AM.  See
     436              :  * comments for ExecRestrPos().
     437              :  *
     438              :  * NOTE: For heap, in the presence of HOT chains, mark/restore only works
     439              :  * correctly if the scan's snapshot is MVCC-safe; that ensures that there's at
     440              :  * most one returnable tuple in each HOT chain, and so restoring the prior
     441              :  * state at the granularity of the index AM is sufficient.  Since the only
     442              :  * current user of mark/restore functionality is nodeMergejoin.c, this
     443              :  * effectively means that merge-join plans only work for MVCC snapshots.  This
     444              :  * could be fixed if necessary, but for now it seems unimportant.
     445              :  * ----------------
     446              :  */
     447              : void
     448        36024 : index_restrpos(IndexScanDesc scan)
     449              : {
     450              :     Assert(IsMVCCLikeSnapshot(scan->xs_snapshot));
     451              : 
     452              :     SCAN_CHECKS;
     453        36024 :     CHECK_SCAN_PROCEDURE(amrestrpos);
     454              : 
     455              :     /* reset table AM state for restoring the marked position */
     456        36024 :     if (scan->xs_heapfetch)
     457        36024 :         table_index_fetch_reset(scan->xs_heapfetch);
     458              : 
     459        36024 :     scan->kill_prior_tuple = false; /* for safety */
     460        36024 :     scan->xs_heap_continue = false;
     461              : 
     462        36024 :     scan->indexRelation->rd_indam->amrestrpos(scan);
     463        36024 : }
     464              : 
     465              : /*
     466              :  * Estimates the shared memory needed for parallel scan, including any
     467              :  * AM-specific parallel scan state.
     468              :  */
     469              : Size
     470           42 : index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys,
     471              :                             Snapshot snapshot)
     472              : {
     473              :     Size        nbytes;
     474              : 
     475           42 :     RELATION_CHECKS;
     476              : 
     477           42 :     nbytes = offsetof(ParallelIndexScanDescData, ps_snapshot_data);
     478           42 :     nbytes = add_size(nbytes, EstimateSnapshotSpace(snapshot));
     479           42 :     nbytes = MAXALIGN(nbytes);
     480              : 
     481              :     /*
     482              :      * If parallel scan index AM interface can't be used (or index AM provides
     483              :      * no such interface), assume there is no AM-specific data needed
     484              :      */
     485           42 :     if (indexRelation->rd_indam->amestimateparallelscan != NULL)
     486           42 :         nbytes = add_size(nbytes,
     487           42 :                           indexRelation->rd_indam->amestimateparallelscan(indexRelation,
     488              :                                                                           nkeys,
     489              :                                                                           norderbys));
     490              : 
     491           42 :     return nbytes;
     492              : }
     493              : 
     494              : /*
     495              :  * index_parallelscan_initialize - initialize parallel scan
     496              :  *
     497              :  * We initialize both the ParallelIndexScanDesc proper and the AM-specific
     498              :  * information which follows it.
     499              :  *
     500              :  * This function calls access method specific initialization routine to
     501              :  * initialize am specific information.  Call this just once in the leader
     502              :  * process; then, individual workers attach via index_beginscan_parallel.
     503              :  */
     504              : void
     505           42 : index_parallelscan_initialize(Relation heapRelation, Relation indexRelation,
     506              :                               Snapshot snapshot,
     507              :                               ParallelIndexScanDesc target)
     508              : {
     509              :     Size        offset;
     510              : 
     511           42 :     RELATION_CHECKS;
     512              : 
     513           42 :     offset = add_size(offsetof(ParallelIndexScanDescData, ps_snapshot_data),
     514              :                       EstimateSnapshotSpace(snapshot));
     515           42 :     offset = MAXALIGN(offset);
     516              : 
     517           42 :     target->ps_locator = heapRelation->rd_locator;
     518           42 :     target->ps_indexlocator = indexRelation->rd_locator;
     519           42 :     target->ps_offset_am = 0;
     520           42 :     SerializeSnapshot(snapshot, target->ps_snapshot_data);
     521              : 
     522              :     /* aminitparallelscan is optional; assume no-op if not provided by AM */
     523           42 :     if (indexRelation->rd_indam->aminitparallelscan != NULL)
     524              :     {
     525              :         void       *amtarget;
     526              : 
     527           42 :         target->ps_offset_am = offset;
     528           42 :         amtarget = OffsetToPointer(target, target->ps_offset_am);
     529           42 :         indexRelation->rd_indam->aminitparallelscan(amtarget);
     530              :     }
     531           42 : }
     532              : 
     533              : /* ----------------
     534              :  *      index_parallelrescan  - (re)start a parallel scan of an index
     535              :  * ----------------
     536              :  */
     537              : void
     538           16 : index_parallelrescan(IndexScanDesc scan)
     539              : {
     540              :     SCAN_CHECKS;
     541              : 
     542              :     /* reset table AM state for rescan */
     543           16 :     if (scan->xs_heapfetch)
     544           16 :         table_index_fetch_reset(scan->xs_heapfetch);
     545              : 
     546              :     /* amparallelrescan is optional; assume no-op if not provided by AM */
     547           16 :     if (scan->indexRelation->rd_indam->amparallelrescan != NULL)
     548           16 :         scan->indexRelation->rd_indam->amparallelrescan(scan);
     549           16 : }
     550              : 
     551              : /*
     552              :  * index_beginscan_parallel - join parallel index scan
     553              :  *
     554              :  * flags is a bitmask of ScanOptions affecting the underlying table scan. No
     555              :  * SO_INTERNAL_FLAGS are permitted.
     556              :  *
     557              :  * Caller must be holding suitable locks on the heap and the index.
     558              :  */
     559              : IndexScanDesc
     560          258 : index_beginscan_parallel(Relation heaprel, Relation indexrel,
     561              :                          IndexScanInstrumentation *instrument,
     562              :                          int nkeys, int norderbys,
     563              :                          ParallelIndexScanDesc pscan,
     564              :                          uint32 flags)
     565              : {
     566              :     Snapshot    snapshot;
     567              :     IndexScanDesc scan;
     568              : 
     569              :     Assert(RelFileLocatorEquals(heaprel->rd_locator, pscan->ps_locator));
     570              :     Assert(RelFileLocatorEquals(indexrel->rd_locator, pscan->ps_indexlocator));
     571              : 
     572          258 :     snapshot = RestoreSnapshot(pscan->ps_snapshot_data);
     573          258 :     RegisterSnapshot(snapshot);
     574          258 :     scan = index_beginscan_internal(indexrel, nkeys, norderbys, snapshot,
     575              :                                     pscan, true);
     576              : 
     577              :     /*
     578              :      * Save additional parameters into the scandesc.  Everything else was set
     579              :      * up by index_beginscan_internal.
     580              :      */
     581          258 :     scan->heapRelation = heaprel;
     582          258 :     scan->xs_snapshot = snapshot;
     583          258 :     scan->instrument = instrument;
     584              : 
     585              :     /* prepare to fetch index matches from table */
     586          258 :     scan->xs_heapfetch = table_index_fetch_begin(heaprel, flags);
     587              : 
     588          258 :     return scan;
     589              : }
     590              : 
     591              : /* ----------------
     592              :  * index_getnext_tid - get the next TID from a scan
     593              :  *
     594              :  * The result is the next TID satisfying the scan keys,
     595              :  * or NULL if no more matching tuples exist.
     596              :  * ----------------
     597              :  */
     598              : ItemPointer
     599     24581096 : index_getnext_tid(IndexScanDesc scan, ScanDirection direction)
     600              : {
     601              :     bool        found;
     602              : 
     603              :     SCAN_CHECKS;
     604     24581096 :     CHECK_SCAN_PROCEDURE(amgettuple);
     605              : 
     606              :     /* XXX: we should assert that a snapshot is pushed or registered */
     607              :     Assert(TransactionIdIsValid(RecentXmin));
     608              : 
     609              :     /*
     610              :      * The AM's amgettuple proc finds the next index entry matching the scan
     611              :      * keys, and puts the TID into scan->xs_heaptid.  It should also set
     612              :      * scan->xs_recheck and possibly scan->xs_itup/scan->xs_hitup, though we
     613              :      * pay no attention to those fields here.
     614              :      */
     615     24581096 :     found = scan->indexRelation->rd_indam->amgettuple(scan, direction);
     616              : 
     617              :     /* Reset kill flag immediately for safety */
     618     24581096 :     scan->kill_prior_tuple = false;
     619     24581096 :     scan->xs_heap_continue = false;
     620              : 
     621              :     /* If we're out of index entries, we're done */
     622     24581096 :     if (!found)
     623              :     {
     624              :         /* reset table AM state */
     625      4493130 :         if (scan->xs_heapfetch)
     626      4493130 :             table_index_fetch_reset(scan->xs_heapfetch);
     627              : 
     628      4493130 :         return NULL;
     629              :     }
     630              :     Assert(ItemPointerIsValid(&scan->xs_heaptid));
     631              : 
     632     20087966 :     pgstat_count_index_tuples(scan->indexRelation, 1);
     633              : 
     634              :     /* Return the TID of the tuple we found. */
     635     20087966 :     return &scan->xs_heaptid;
     636              : }
     637              : 
     638              : /* ----------------
     639              :  *      index_fetch_heap - get the scan's next heap tuple
     640              :  *
     641              :  * The result is a visible heap tuple associated with the index TID most
     642              :  * recently fetched by index_getnext_tid, or NULL if no more matching tuples
     643              :  * exist.  (There can be more than one matching tuple because of HOT chains,
     644              :  * although when using an MVCC snapshot it should be impossible for more than
     645              :  * one such tuple to exist.)
     646              :  *
     647              :  * On success, the buffer containing the heap tup is pinned (the pin will be
     648              :  * dropped in a future index_getnext_tid, index_fetch_heap or index_endscan
     649              :  * call).
     650              :  *
     651              :  * Note: caller must check scan->xs_recheck, and perform rechecking of the
     652              :  * scan keys if required.  We do not do that here because we don't have
     653              :  * enough information to do it efficiently in the general case.
     654              :  * ----------------
     655              :  */
     656              : bool
     657     16910985 : index_fetch_heap(IndexScanDesc scan, TupleTableSlot *slot)
     658              : {
     659     16910985 :     bool        all_dead = false;
     660              :     bool        found;
     661              : 
     662     16910985 :     found = table_index_fetch_tuple(scan->xs_heapfetch, &scan->xs_heaptid,
     663              :                                     scan->xs_snapshot, slot,
     664              :                                     &scan->xs_heap_continue, &all_dead);
     665              : 
     666     16910980 :     if (found)
     667     16192821 :         pgstat_count_heap_fetch(scan->indexRelation);
     668              : 
     669              :     /*
     670              :      * If we scanned a whole HOT chain and found only dead tuples, tell index
     671              :      * AM to kill its entry for that TID (this will take effect in the next
     672              :      * amgettuple call, in index_getnext_tid).  We do not do this when in
     673              :      * recovery because it may violate MVCC to do so.  See comments in
     674              :      * RelationGetIndexScan().
     675              :      */
     676     16910980 :     if (!scan->xactStartedInRecovery)
     677     16633327 :         scan->kill_prior_tuple = all_dead;
     678              : 
     679     16910980 :     return found;
     680              : }
     681              : 
     682              : /* ----------------
     683              :  *      index_getnext_slot - get the next tuple from a scan
     684              :  *
     685              :  * The result is true if a tuple satisfying the scan keys and the snapshot was
     686              :  * found, false otherwise.  The tuple is stored in the specified slot.
     687              :  *
     688              :  * On success, resources (like buffer pins) are likely to be held, and will be
     689              :  * dropped by a future index_getnext_tid, index_fetch_heap or index_endscan
     690              :  * call).
     691              :  *
     692              :  * Note: caller must check scan->xs_recheck, and perform rechecking of the
     693              :  * scan keys if required.  We do not do that here because we don't have
     694              :  * enough information to do it efficiently in the general case.
     695              :  * ----------------
     696              :  */
     697              : bool
     698     20059829 : index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot)
     699              : {
     700              :     for (;;)
     701              :     {
     702     20655170 :         if (!scan->xs_heap_continue)
     703              :         {
     704              :             ItemPointer tid;
     705              : 
     706              :             /* Time to fetch the next TID from the index */
     707     20556479 :             tid = index_getnext_tid(scan, direction);
     708              : 
     709              :             /* If we're out of index entries, we're done */
     710     20556479 :             if (tid == NULL)
     711      4358158 :                 break;
     712              : 
     713              :             Assert(ItemPointerEquals(tid, &scan->xs_heaptid));
     714              :         }
     715              : 
     716              :         /*
     717              :          * Fetch the next (or only) visible heap tuple for this index entry.
     718              :          * If we don't find anything, loop around and grab the next TID from
     719              :          * the index.
     720              :          */
     721              :         Assert(ItemPointerIsValid(&scan->xs_heaptid));
     722     16297012 :         if (index_fetch_heap(scan, slot))
     723     15701666 :             return true;
     724              :     }
     725              : 
     726      4358158 :     return false;
     727              : }
     728              : 
     729              : /* ----------------
     730              :  *      index_getbitmap - get all tuples at once from an index scan
     731              :  *
     732              :  * Adds the TIDs of all heap tuples satisfying the scan keys to a bitmap.
     733              :  * Since there's no interlock between the index scan and the eventual heap
     734              :  * access, this is only safe to use with MVCC-based snapshots: the heap
     735              :  * item slot could have been replaced by a newer tuple by the time we get
     736              :  * to it.
     737              :  *
     738              :  * Returns the number of matching tuples found.  (Note: this might be only
     739              :  * approximate, so it should only be used for statistical purposes.)
     740              :  * ----------------
     741              :  */
     742              : int64
     743        16085 : index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
     744              : {
     745              :     int64       ntids;
     746              : 
     747              :     SCAN_CHECKS;
     748        16085 :     CHECK_SCAN_PROCEDURE(amgetbitmap);
     749              : 
     750              :     /* just make sure this is false... */
     751        16085 :     scan->kill_prior_tuple = false;
     752              : 
     753              :     /*
     754              :      * have the am's getbitmap proc do all the work.
     755              :      */
     756        16085 :     ntids = scan->indexRelation->rd_indam->amgetbitmap(scan, bitmap);
     757              : 
     758        16085 :     pgstat_count_index_tuples(scan->indexRelation, ntids);
     759              : 
     760        16085 :     return ntids;
     761              : }
     762              : 
     763              : /* ----------------
     764              :  *      index_bulk_delete - do mass deletion of index entries
     765              :  *
     766              :  *      callback routine tells whether a given main-heap tuple is
     767              :  *      to be deleted
     768              :  *
     769              :  *      return value is an optional palloc'd struct of statistics
     770              :  * ----------------
     771              :  */
     772              : IndexBulkDeleteResult *
     773         1963 : index_bulk_delete(IndexVacuumInfo *info,
     774              :                   IndexBulkDeleteResult *istat,
     775              :                   IndexBulkDeleteCallback callback,
     776              :                   void *callback_state)
     777              : {
     778         1963 :     Relation    indexRelation = info->index;
     779              : 
     780         1963 :     RELATION_CHECKS;
     781         1963 :     CHECK_REL_PROCEDURE(ambulkdelete);
     782              : 
     783         1963 :     return indexRelation->rd_indam->ambulkdelete(info, istat,
     784              :                                                  callback, callback_state);
     785              : }
     786              : 
     787              : /* ----------------
     788              :  *      index_vacuum_cleanup - do post-deletion cleanup of an index
     789              :  *
     790              :  *      return value is an optional palloc'd struct of statistics
     791              :  * ----------------
     792              :  */
     793              : IndexBulkDeleteResult *
     794       150757 : index_vacuum_cleanup(IndexVacuumInfo *info,
     795              :                      IndexBulkDeleteResult *istat)
     796              : {
     797       150757 :     Relation    indexRelation = info->index;
     798              : 
     799       150757 :     RELATION_CHECKS;
     800       150757 :     CHECK_REL_PROCEDURE(amvacuumcleanup);
     801              : 
     802       150757 :     return indexRelation->rd_indam->amvacuumcleanup(info, istat);
     803              : }
     804              : 
     805              : /* ----------------
     806              :  *      index_can_return
     807              :  *
     808              :  *      Does the index access method support index-only scans for the given
     809              :  *      column?
     810              :  * ----------------
     811              :  */
     812              : bool
     813      1097146 : index_can_return(Relation indexRelation, int attno)
     814              : {
     815      1097146 :     RELATION_CHECKS;
     816              : 
     817              :     /* amcanreturn is optional; assume false if not provided by AM */
     818      1097146 :     if (indexRelation->rd_indam->amcanreturn == NULL)
     819       226925 :         return false;
     820              : 
     821       870221 :     return indexRelation->rd_indam->amcanreturn(indexRelation, attno);
     822              : }
     823              : 
     824              : /* ----------------
     825              :  *      index_getprocid
     826              :  *
     827              :  *      Index access methods typically require support routines that are
     828              :  *      not directly the implementation of any WHERE-clause query operator
     829              :  *      and so cannot be kept in pg_amop.  Instead, such routines are kept
     830              :  *      in pg_amproc.  These registered procedure OIDs are assigned numbers
     831              :  *      according to a convention established by the access method.
     832              :  *      The general index code doesn't know anything about the routines
     833              :  *      involved; it just builds an ordered list of them for
     834              :  *      each attribute on which an index is defined.
     835              :  *
     836              :  *      As of Postgres 8.3, support routines within an operator family
     837              :  *      are further subdivided by the "left type" and "right type" of the
     838              :  *      query operator(s) that they support.  The "default" functions for a
     839              :  *      particular indexed attribute are those with both types equal to
     840              :  *      the index opclass' opcintype (note that this is subtly different
     841              :  *      from the indexed attribute's own type: it may be a binary-compatible
     842              :  *      type instead).  Only the default functions are stored in relcache
     843              :  *      entries --- access methods can use the syscache to look up non-default
     844              :  *      functions.
     845              :  *
     846              :  *      This routine returns the requested default procedure OID for a
     847              :  *      particular indexed attribute.
     848              :  * ----------------
     849              :  */
     850              : RegProcedure
     851      1342296 : index_getprocid(Relation irel,
     852              :                 AttrNumber attnum,
     853              :                 uint16 procnum)
     854              : {
     855              :     RegProcedure *loc;
     856              :     int         nproc;
     857              :     int         procindex;
     858              : 
     859      1342296 :     nproc = irel->rd_indam->amsupport;
     860              : 
     861              :     Assert(procnum > 0 && procnum <= (uint16) nproc);
     862              : 
     863      1342296 :     procindex = (nproc * (attnum - 1)) + (procnum - 1);
     864              : 
     865      1342296 :     loc = irel->rd_support;
     866              : 
     867              :     Assert(loc != NULL);
     868              : 
     869      1342296 :     return loc[procindex];
     870              : }
     871              : 
     872              : /* ----------------
     873              :  *      index_getprocinfo
     874              :  *
     875              :  *      This routine allows index AMs to keep fmgr lookup info for
     876              :  *      support procs in the relcache.  As above, only the "default"
     877              :  *      functions for any particular indexed attribute are cached.
     878              :  *
     879              :  * Note: the return value points into cached data that will be lost during
     880              :  * any relcache rebuild!  Therefore, either use the callinfo right away,
     881              :  * or save it only after having acquired some type of lock on the index rel.
     882              :  * ----------------
     883              :  */
     884              : FmgrInfo *
     885     31049693 : index_getprocinfo(Relation irel,
     886              :                   AttrNumber attnum,
     887              :                   uint16 procnum)
     888              : {
     889              :     FmgrInfo   *locinfo;
     890              :     int         nproc;
     891              :     int         optsproc;
     892              :     int         procindex;
     893              : 
     894     31049693 :     nproc = irel->rd_indam->amsupport;
     895     31049693 :     optsproc = irel->rd_indam->amoptsprocnum;
     896              : 
     897              :     Assert(procnum > 0 && procnum <= (uint16) nproc);
     898              : 
     899     31049693 :     procindex = (nproc * (attnum - 1)) + (procnum - 1);
     900              : 
     901     31049693 :     locinfo = irel->rd_supportinfo;
     902              : 
     903              :     Assert(locinfo != NULL);
     904              : 
     905     31049693 :     locinfo += procindex;
     906              : 
     907              :     /* Initialize the lookup info if first time through */
     908     31049693 :     if (locinfo->fn_oid == InvalidOid)
     909              :     {
     910       683502 :         RegProcedure *loc = irel->rd_support;
     911              :         RegProcedure procId;
     912              : 
     913              :         Assert(loc != NULL);
     914              : 
     915       683502 :         procId = loc[procindex];
     916              : 
     917              :         /*
     918              :          * Complain if function was not found during IndexSupportInitialize.
     919              :          * This should not happen unless the system tables contain bogus
     920              :          * entries for the index opclass.  (If an AM wants to allow a support
     921              :          * function to be optional, it can use index_getprocid.)
     922              :          */
     923       683502 :         if (!RegProcedureIsValid(procId))
     924            0 :             elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
     925              :                  procnum, attnum, RelationGetRelationName(irel));
     926              : 
     927       683502 :         fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
     928              : 
     929       683502 :         if (procnum != optsproc)
     930              :         {
     931              :             /* Initialize locinfo->fn_expr with opclass options Const */
     932       682356 :             bytea     **attoptions = RelationGetIndexAttOptions(irel, false);
     933       682356 :             MemoryContext oldcxt = MemoryContextSwitchTo(irel->rd_indexcxt);
     934              : 
     935       682356 :             set_fn_opclass_options(locinfo, attoptions[attnum - 1]);
     936              : 
     937       682356 :             MemoryContextSwitchTo(oldcxt);
     938              :         }
     939              :     }
     940              : 
     941     31049693 :     return locinfo;
     942              : }
     943              : 
     944              : /* ----------------
     945              :  *      index_store_float8_orderby_distances
     946              :  *
     947              :  *      Convert AM distance function's results (that can be inexact)
     948              :  *      to ORDER BY types and save them into xs_orderbyvals/xs_orderbynulls
     949              :  *      for a possible recheck.
     950              :  * ----------------
     951              :  */
     952              : void
     953       242999 : index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
     954              :                                      IndexOrderByDistance *distances,
     955              :                                      bool recheckOrderBy)
     956              : {
     957              :     int         i;
     958              : 
     959              :     Assert(distances || !recheckOrderBy);
     960              : 
     961       242999 :     scan->xs_recheckorderby = recheckOrderBy;
     962              : 
     963       486010 :     for (i = 0; i < scan->numberOfOrderBys; i++)
     964              :     {
     965       243011 :         if (orderByTypes[i] == FLOAT8OID)
     966              :         {
     967       242946 :             if (distances && !distances[i].isnull)
     968              :             {
     969       242906 :                 scan->xs_orderbyvals[i] = Float8GetDatum(distances[i].value);
     970       242906 :                 scan->xs_orderbynulls[i] = false;
     971              :             }
     972              :             else
     973              :             {
     974           40 :                 scan->xs_orderbyvals[i] = (Datum) 0;
     975           40 :                 scan->xs_orderbynulls[i] = true;
     976              :             }
     977              :         }
     978           65 :         else if (orderByTypes[i] == FLOAT4OID)
     979              :         {
     980              :             /* convert distance function's result to ORDER BY type */
     981           35 :             if (distances && !distances[i].isnull)
     982              :             {
     983           35 :                 scan->xs_orderbyvals[i] = Float4GetDatum((float4) distances[i].value);
     984           35 :                 scan->xs_orderbynulls[i] = false;
     985              :             }
     986              :             else
     987              :             {
     988            0 :                 scan->xs_orderbyvals[i] = (Datum) 0;
     989            0 :                 scan->xs_orderbynulls[i] = true;
     990              :             }
     991              :         }
     992              :         else
     993              :         {
     994              :             /*
     995              :              * If the ordering operator's return value is anything else, we
     996              :              * don't know how to convert the float8 bound calculated by the
     997              :              * distance function to that.  The executor won't actually need
     998              :              * the order by values we return here, if there are no lossy
     999              :              * results, so only insist on converting if the *recheck flag is
    1000              :              * set.
    1001              :              */
    1002           30 :             if (scan->xs_recheckorderby)
    1003            0 :                 elog(ERROR, "ORDER BY operator must return float8 or float4 if the distance function is lossy");
    1004           30 :             scan->xs_orderbynulls[i] = true;
    1005              :         }
    1006              :     }
    1007       242999 : }
    1008              : 
    1009              : /* ----------------
    1010              :  *      index_opclass_options
    1011              :  *
    1012              :  *      Parse opclass-specific options for index column.
    1013              :  * ----------------
    1014              :  */
    1015              : bytea *
    1016       720866 : index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
    1017              :                       bool validate)
    1018              : {
    1019       720866 :     int         amoptsprocnum = indrel->rd_indam->amoptsprocnum;
    1020       720866 :     Oid         procid = InvalidOid;
    1021              :     FmgrInfo   *procinfo;
    1022              :     local_relopts relopts;
    1023              : 
    1024              :     /* fetch options support procedure if specified */
    1025       720866 :     if (amoptsprocnum != 0)
    1026       720832 :         procid = index_getprocid(indrel, attnum, amoptsprocnum);
    1027              : 
    1028       720866 :     if (!OidIsValid(procid))
    1029              :     {
    1030              :         Oid         opclass;
    1031              :         Datum       indclassDatum;
    1032              :         oidvector  *indclass;
    1033              : 
    1034       719257 :         if (!DatumGetPointer(attoptions))
    1035       719253 :             return NULL;        /* ok, no options, no procedure */
    1036              : 
    1037              :         /*
    1038              :          * Report an error if the opclass's options-parsing procedure does not
    1039              :          * exist but the opclass options are specified.
    1040              :          */
    1041            4 :         indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indrel->rd_indextuple,
    1042              :                                                Anum_pg_index_indclass);
    1043            4 :         indclass = (oidvector *) DatumGetPointer(indclassDatum);
    1044            4 :         opclass = indclass->values[attnum - 1];
    1045              : 
    1046            4 :         ereport(ERROR,
    1047              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    1048              :                  errmsg("operator class %s has no options",
    1049              :                         generate_opclass_name(opclass))));
    1050              :     }
    1051              : 
    1052         1609 :     init_local_reloptions(&relopts, 0);
    1053              : 
    1054         1609 :     procinfo = index_getprocinfo(indrel, attnum, amoptsprocnum);
    1055              : 
    1056         1609 :     (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
    1057              : 
    1058         1609 :     return build_local_reloptions(&relopts, attoptions, validate);
    1059              : }
        

Generated by: LCOV version 2.0-1