LCOV - code coverage report
Current view: top level - src/backend/access/index - indexam.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 97.2 % 215 209
Test Date: 2026-09-23 18:15:57 Functions: 100.0 % 25 25
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 50.5 % 192 97

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

Generated by: LCOV version 2.0-1