Coverage Report

Created: 2026-11-12 07:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/access/index/indexam.c
Line
Count
Source
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
0
#define RELATION_CHECKS \
74
0
do { \
75
0
  Assert(RelationIsValid(indexRelation)); \
76
0
  Assert(indexRelation->rd_indam); \
77
0
  if (unlikely(ReindexIsProcessingIndex(RelationGetRelid(indexRelation)))) \
78
0
    ereport(ERROR, \
79
0
        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
80
0
         errmsg("cannot access index \"%s\" while it is being reindexed", \
81
0
            RelationGetRelationName(indexRelation)))); \
82
0
} while(0)
83
84
0
#define SCAN_CHECKS \
85
0
( \
86
0
  AssertMacro(scan), \
87
0
  AssertMacro(RelationIsValid(scan->indexRelation)), \
88
0
  AssertMacro(scan->indexRelation->rd_indam) \
89
0
)
90
91
0
#define CHECK_REL_PROCEDURE(pname) \
92
0
do { \
93
0
  if (indexRelation->rd_indam->pname == NULL) \
94
0
    elog(ERROR, "function \"%s\" is not defined for index \"%s\"", \
95
0
       CppAsString(pname), RelationGetRelationName(indexRelation)); \
96
0
} while(0)
97
98
0
#define CHECK_SCAN_PROCEDURE(pname) \
99
0
do { \
100
0
  if (scan->indexRelation->rd_indam->pname == NULL) \
101
0
    elog(ERROR, "function \"%s\" is not defined for index \"%s\"", \
102
0
       CppAsString(pname), RelationGetRelationName(scan->indexRelation)); \
103
0
} 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
index_open(Oid relationId, LOCKMODE lockmode)
139
0
{
140
0
  Relation  r;
141
142
0
  r = relation_open(relationId, lockmode);
143
144
0
  validate_relation_as_index(r);
145
146
0
  return r;
147
0
}
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
try_index_open(Oid relationId, LOCKMODE lockmode)
158
0
{
159
0
  Relation  r;
160
161
0
  r = try_relation_open(relationId, lockmode);
162
163
  /* leave if index does not exist */
164
0
  if (!r)
165
0
    return NULL;
166
167
0
  validate_relation_as_index(r);
168
169
0
  return r;
170
0
}
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
index_close(Relation relation, LOCKMODE lockmode)
183
0
{
184
0
  LockRelId relid = relation->rd_lockInfo.lockRelId;
185
186
0
  Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
187
188
  /* The relcache does the real work... */
189
0
  RelationClose(relation);
190
191
0
  if (lockmode != NoLock)
192
0
    UnlockRelationId(&relid, lockmode);
193
0
}
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
validate_relation_as_index(Relation r)
203
0
{
204
0
  if (r->rd_rel->relkind != RELKIND_INDEX &&
205
0
    r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
206
0
    ereport(ERROR,
207
0
        (errcode(ERRCODE_WRONG_OBJECT_TYPE),
208
0
         errmsg("\"%s\" is not an index",
209
0
            RelationGetRelationName(r))));
210
0
}
211
212
213
/* ----------------
214
 *    index_insert - insert an index tuple into a relation
215
 * ----------------
216
 */
217
bool
218
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
0
{
227
0
  RELATION_CHECKS;
228
0
  CHECK_REL_PROCEDURE(aminsert);
229
230
0
  if (!(indexRelation->rd_indam->ampredlocks))
231
0
    CheckForSerializableConflictIn(indexRelation,
232
0
                     (ItemPointer) NULL,
233
0
                     InvalidBlockNumber);
234
235
0
  return indexRelation->rd_indam->aminsert(indexRelation, values, isnull,
236
0
                       heap_t_ctid, heapRelation,
237
0
                       checkUnique, indexUnchanged,
238
0
                       indexInfo);
239
0
}
240
241
/* -------------------------
242
 *    index_insert_cleanup - clean up after all index inserts are done
243
 * -------------------------
244
 */
245
void
246
index_insert_cleanup(Relation indexRelation,
247
           IndexInfo *indexInfo)
248
0
{
249
0
  RELATION_CHECKS;
250
251
0
  if (indexRelation->rd_indam->aminsertcleanup)
252
0
    indexRelation->rd_indam->aminsertcleanup(indexRelation, indexInfo);
253
0
}
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
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
0
{
269
0
  Assert(snapshot != InvalidSnapshot);
270
0
  pg_assume(heapRelation != NULL);
271
272
  /* Check that a historic snapshot is not used for non-catalog tables */
273
0
  if (IsHistoricMVCCSnapshot(snapshot) &&
274
0
    !RelationIsAccessibleInLogicalDecoding(heapRelation))
275
0
  {
276
0
    ereport(ERROR,
277
0
        (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
278
0
         errmsg("cannot query non-catalog table \"%s\" during logical decoding",
279
0
            RelationGetRelationName(heapRelation))));
280
0
  }
281
282
0
  return index_beginscan_internal(indexRelation, heapRelation,
283
0
                  nkeys, norderbys,
284
0
                  snapshot, NULL, instrument,
285
0
                  index_only_scan, false, flags);
286
0
}
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
index_beginscan_bitmap(Relation indexRelation,
296
             Snapshot snapshot,
297
             IndexScanInstrumentation *instrument,
298
             int nkeys)
299
0
{
300
0
  Assert(snapshot != InvalidSnapshot);
301
0
  Assert(IsMVCCLikeSnapshot(snapshot));
302
303
0
  return index_beginscan_internal(indexRelation, NULL, nkeys, 0, snapshot,
304
0
                  NULL, instrument, false, false, SO_NONE);
305
0
}
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
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
0
{
319
0
  IndexScanDesc scan;
320
321
0
  RELATION_CHECKS;
322
0
  CHECK_REL_PROCEDURE(ambeginscan);
323
324
0
  if (!(indexRelation->rd_indam->ampredlocks))
325
0
    PredicateLockRelation(indexRelation, snapshot);
326
327
  /*
328
   * We hold a reference count to the relcache entry throughout the scan.
329
   */
330
0
  RelationIncrementReferenceCount(indexRelation);
331
332
  /*
333
   * Tell the AM to open a scan.
334
   */
335
0
  scan = indexRelation->rd_indam->ambeginscan(indexRelation, nkeys,
336
0
                        norderbys);
337
  /* Initialize information for parallel scan. */
338
0
  scan->parallel_scan = pscan;
339
0
  scan->xs_temp_snap = temp_snap;
340
341
0
  scan->xs_snapshot = snapshot;
342
0
  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
0
  if (heapRelation != NULL)
349
0
  {
350
0
    scan->heapRelation = heapRelation;
351
0
    scan->xs_want_itup = index_only_scan;
352
0
    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
0
    if (index_only_scan)
363
0
    {
364
0
      int     indnkeyatts = indexRelation->rd_index->indnkeyatts;
365
0
      int     namecount = 0;
366
367
0
      for (int attnum = 0; attnum < indnkeyatts; attnum++)
368
0
      {
369
0
        if (TupleDescAttr(indexRelation->rd_att, attnum)->atttypid == CSTRINGOID &&
370
0
          indexRelation->rd_opcintype[attnum] == NAMEOID)
371
0
          namecount++;
372
0
      }
373
374
0
      if (unlikely(namecount > 0))
375
0
      {
376
0
        int     idx = 0;
377
378
0
        scan->xs_name_cstring_attnums = palloc_array(AttrNumber, namecount);
379
0
        for (int attnum = 0; attnum < indnkeyatts; attnum++)
380
0
        {
381
0
          if (TupleDescAttr(indexRelation->rd_att, attnum)->atttypid == CSTRINGOID &&
382
0
            indexRelation->rd_opcintype[attnum] == NAMEOID)
383
0
            scan->xs_name_cstring_attnums[idx++] = (AttrNumber) attnum;
384
0
        }
385
386
0
        scan->xs_name_cstring_buf = palloc(namecount * NAMEDATALEN);
387
0
        scan->xs_name_cstring_count = namecount;
388
0
      }
389
0
    }
390
391
    /* set up table AM state for the index scan (sets xs_table_opaque) */
392
0
    table_index_scan_begin(scan, flags);
393
394
    /* table AM must set these for us */
395
0
    Assert(scan->xs_getnext_slot != NULL && scan->xs_table_opaque != NULL);
396
0
  }
397
398
0
  return scan;
399
0
}
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
index_rescan(IndexScanDesc scan,
415
       ScanKey keys, int nkeys,
416
       ScanKey orderbys, int norderbys)
417
0
{
418
0
  SCAN_CHECKS;
419
0
  CHECK_SCAN_PROCEDURE(amrescan);
420
421
0
  Assert(nkeys == scan->numberOfKeys);
422
0
  Assert(norderbys == scan->numberOfOrderBys);
423
424
  /* reset table AM state for rescan */
425
0
  if (scan->xs_table_opaque)
426
0
    table_index_scan_reset(scan);
427
428
0
  scan->kill_prior_tuple = false; /* for safety */
429
0
  scan->xs_heap_continue = false;
430
431
0
  scan->indexRelation->rd_indam->amrescan(scan, keys, nkeys,
432
0
                      orderbys, norderbys);
433
0
}
434
435
/* ----------------
436
 *    index_endscan - end a scan
437
 * ----------------
438
 */
439
void
440
index_endscan(IndexScanDesc scan)
441
0
{
442
0
  SCAN_CHECKS;
443
0
  CHECK_SCAN_PROCEDURE(amendscan);
444
445
  /* Release resources (like buffer pins) from table accesses */
446
0
  if (scan->xs_table_opaque)
447
0
    table_index_scan_end(scan);
448
449
  /* End the AM's scan */
450
0
  scan->indexRelation->rd_indam->amendscan(scan);
451
452
  /* Release index refcount acquired by index_beginscan */
453
0
  RelationDecrementReferenceCount(scan->indexRelation);
454
455
0
  if (scan->xs_temp_snap)
456
0
    UnregisterSnapshot(scan->xs_snapshot);
457
458
  /* Release the scan data structure itself */
459
0
  IndexScanEnd(scan);
460
0
}
461
462
/* ----------------
463
 *    index_markpos  - mark a scan position
464
 * ----------------
465
 */
466
void
467
index_markpos(IndexScanDesc scan)
468
0
{
469
0
  SCAN_CHECKS;
470
0
  CHECK_SCAN_PROCEDURE(ammarkpos);
471
472
0
  scan->indexRelation->rd_indam->ammarkpos(scan);
473
0
}
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
index_restrpos(IndexScanDesc scan)
492
0
{
493
0
  Assert(IsMVCCLikeSnapshot(scan->xs_snapshot));
494
495
0
  SCAN_CHECKS;
496
0
  CHECK_SCAN_PROCEDURE(amrestrpos);
497
498
  /* reset table AM state for restoring the marked position */
499
0
  if (scan->xs_table_opaque)
500
0
    table_index_scan_reset(scan);
501
502
0
  scan->kill_prior_tuple = false; /* for safety */
503
0
  scan->xs_heap_continue = false;
504
505
0
  scan->indexRelation->rd_indam->amrestrpos(scan);
506
0
}
507
508
/*
509
 * Estimates the shared memory needed for parallel scan, including any
510
 * AM-specific parallel scan state.
511
 */
512
Size
513
index_parallelscan_estimate(Relation indexRelation, int nkeys, int norderbys,
514
              Snapshot snapshot)
515
0
{
516
0
  Size    nbytes;
517
518
0
  RELATION_CHECKS;
519
520
0
  nbytes = offsetof(ParallelIndexScanDescData, ps_snapshot_data);
521
0
  nbytes = add_size(nbytes, EstimateSnapshotSpace(snapshot));
522
0
  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
0
  if (indexRelation->rd_indam->amestimateparallelscan != NULL)
529
0
    nbytes = add_size(nbytes,
530
0
              indexRelation->rd_indam->amestimateparallelscan(indexRelation,
531
0
                                      nkeys,
532
0
                                      norderbys));
533
534
0
  return nbytes;
535
0
}
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
index_parallelscan_initialize(Relation heapRelation, Relation indexRelation,
549
                Snapshot snapshot,
550
                ParallelIndexScanDesc target)
551
0
{
552
0
  Size    offset;
553
554
0
  RELATION_CHECKS;
555
556
0
  offset = add_size(offsetof(ParallelIndexScanDescData, ps_snapshot_data),
557
0
            EstimateSnapshotSpace(snapshot));
558
0
  offset = MAXALIGN(offset);
559
560
0
  target->ps_locator = heapRelation->rd_locator;
561
0
  target->ps_indexlocator = indexRelation->rd_locator;
562
0
  target->ps_offset_am = 0;
563
0
  SerializeSnapshot(snapshot, target->ps_snapshot_data);
564
565
  /* aminitparallelscan is optional; assume no-op if not provided by AM */
566
0
  if (indexRelation->rd_indam->aminitparallelscan != NULL)
567
0
  {
568
0
    void     *amtarget;
569
570
0
    target->ps_offset_am = offset;
571
0
    amtarget = OffsetToPointer(target, target->ps_offset_am);
572
0
    indexRelation->rd_indam->aminitparallelscan(amtarget);
573
0
  }
574
0
}
575
576
/* ----------------
577
 *    index_parallelrescan  - (re)start a parallel scan of an index
578
 * ----------------
579
 */
580
void
581
index_parallelrescan(IndexScanDesc scan)
582
0
{
583
0
  SCAN_CHECKS;
584
585
  /* reset table AM state for rescan */
586
0
  if (scan->xs_table_opaque)
587
0
    table_index_scan_reset(scan);
588
589
  /* amparallelrescan is optional; assume no-op if not provided by AM */
590
0
  if (scan->indexRelation->rd_indam->amparallelrescan != NULL)
591
0
    scan->indexRelation->rd_indam->amparallelrescan(scan);
592
0
}
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
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
0
{
610
0
  Snapshot  snapshot;
611
612
0
  Assert(RelFileLocatorEquals(heaprel->rd_locator, pscan->ps_locator));
613
0
  Assert(RelFileLocatorEquals(indexrel->rd_locator, pscan->ps_indexlocator));
614
0
  pg_assume(heaprel != NULL);
615
616
0
  snapshot = RestoreSnapshot(pscan->ps_snapshot_data);
617
0
  RegisterSnapshot(snapshot);
618
619
0
  return index_beginscan_internal(indexrel, heaprel, nkeys, norderbys,
620
0
                  snapshot, pscan, instrument,
621
0
                  index_only_scan, true, flags);
622
0
}
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
index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
639
0
{
640
0
  int64   ntids;
641
642
0
  SCAN_CHECKS;
643
0
  CHECK_SCAN_PROCEDURE(amgetbitmap);
644
645
  /* just make sure this is false... */
646
0
  scan->kill_prior_tuple = false;
647
648
  /*
649
   * have the am's getbitmap proc do all the work.
650
   */
651
0
  ntids = scan->indexRelation->rd_indam->amgetbitmap(scan, bitmap);
652
653
0
  pgstat_count_index_tuples(scan->indexRelation, ntids);
654
655
0
  return ntids;
656
0
}
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
index_bulk_delete(IndexVacuumInfo *info,
669
          IndexBulkDeleteResult *istat,
670
          IndexBulkDeleteCallback callback,
671
          void *callback_state)
672
0
{
673
0
  Relation  indexRelation = info->index;
674
675
0
  RELATION_CHECKS;
676
0
  CHECK_REL_PROCEDURE(ambulkdelete);
677
678
0
  return indexRelation->rd_indam->ambulkdelete(info, istat,
679
0
                         callback, callback_state);
680
0
}
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
index_vacuum_cleanup(IndexVacuumInfo *info,
690
           IndexBulkDeleteResult *istat)
691
0
{
692
0
  Relation  indexRelation = info->index;
693
694
0
  RELATION_CHECKS;
695
0
  CHECK_REL_PROCEDURE(amvacuumcleanup);
696
697
0
  return indexRelation->rd_indam->amvacuumcleanup(info, istat);
698
0
}
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
index_can_return(Relation indexRelation, int attno)
709
0
{
710
0
  RELATION_CHECKS;
711
712
  /* amcanreturn is optional; assume false if not provided by AM */
713
0
  if (indexRelation->rd_indam->amcanreturn == NULL)
714
0
    return false;
715
716
0
  return indexRelation->rd_indam->amcanreturn(indexRelation, attno);
717
0
}
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
index_getprocid(Relation irel,
747
        AttrNumber attnum,
748
        uint16 procnum)
749
0
{
750
0
  RegProcedure *loc;
751
0
  int     nproc;
752
0
  int     procindex;
753
754
0
  nproc = irel->rd_indam->amsupport;
755
756
0
  Assert(procnum > 0 && procnum <= (uint16) nproc);
757
758
0
  procindex = (nproc * (attnum - 1)) + (procnum - 1);
759
760
0
  loc = irel->rd_support;
761
762
0
  Assert(loc != NULL);
763
764
0
  return loc[procindex];
765
0
}
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
index_getprocinfo(Relation irel,
781
          AttrNumber attnum,
782
          uint16 procnum)
783
0
{
784
0
  FmgrInfo   *locinfo;
785
0
  int     nproc;
786
0
  int     optsproc;
787
0
  int     procindex;
788
789
0
  nproc = irel->rd_indam->amsupport;
790
0
  optsproc = irel->rd_indam->amoptsprocnum;
791
792
0
  Assert(procnum > 0 && procnum <= (uint16) nproc);
793
794
0
  procindex = (nproc * (attnum - 1)) + (procnum - 1);
795
796
0
  locinfo = irel->rd_supportinfo;
797
798
0
  Assert(locinfo != NULL);
799
800
0
  locinfo += procindex;
801
802
  /* Initialize the lookup info if first time through */
803
0
  if (locinfo->fn_oid == InvalidOid)
804
0
  {
805
0
    RegProcedure *loc = irel->rd_support;
806
0
    RegProcedure procId;
807
808
0
    Assert(loc != NULL);
809
810
0
    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
0
    if (!RegProcedureIsValid(procId))
819
0
      elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
820
0
         procnum, attnum, RelationGetRelationName(irel));
821
822
0
    fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
823
824
0
    if (procnum != optsproc)
825
0
    {
826
      /* Initialize locinfo->fn_expr with opclass options Const */
827
0
      bytea   **attoptions = RelationGetIndexAttOptions(irel, false);
828
0
      MemoryContext oldcxt = MemoryContextSwitchTo(irel->rd_indexcxt);
829
830
0
      set_fn_opclass_options(locinfo, attoptions[attnum - 1]);
831
832
0
      MemoryContextSwitchTo(oldcxt);
833
0
    }
834
0
  }
835
836
0
  return locinfo;
837
0
}
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
index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes,
849
                   IndexOrderByDistance *distances,
850
                   bool recheckOrderBy)
851
0
{
852
0
  int     i;
853
854
0
  Assert(distances || !recheckOrderBy);
855
856
0
  scan->xs_recheckorderby = recheckOrderBy;
857
858
0
  for (i = 0; i < scan->numberOfOrderBys; i++)
859
0
  {
860
0
    if (orderByTypes[i] == FLOAT8OID)
861
0
    {
862
0
      if (distances && !distances[i].isnull)
863
0
      {
864
0
        scan->xs_orderbyvals[i] = Float8GetDatum(distances[i].value);
865
0
        scan->xs_orderbynulls[i] = false;
866
0
      }
867
0
      else
868
0
      {
869
0
        scan->xs_orderbyvals[i] = (Datum) 0;
870
0
        scan->xs_orderbynulls[i] = true;
871
0
      }
872
0
    }
873
0
    else if (orderByTypes[i] == FLOAT4OID)
874
0
    {
875
      /* convert distance function's result to ORDER BY type */
876
0
      if (distances && !distances[i].isnull)
877
0
      {
878
0
        scan->xs_orderbyvals[i] = Float4GetDatum((float4) distances[i].value);
879
0
        scan->xs_orderbynulls[i] = false;
880
0
      }
881
0
      else
882
0
      {
883
0
        scan->xs_orderbyvals[i] = (Datum) 0;
884
0
        scan->xs_orderbynulls[i] = true;
885
0
      }
886
0
    }
887
0
    else
888
0
    {
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
0
      if (scan->xs_recheckorderby)
898
0
        elog(ERROR, "ORDER BY operator must return float8 or float4 if the distance function is lossy");
899
0
      scan->xs_orderbynulls[i] = true;
900
0
    }
901
0
  }
902
0
}
903
904
/* ----------------
905
 *      index_opclass_options
906
 *
907
 *      Parse opclass-specific options for index column.
908
 * ----------------
909
 */
910
bytea *
911
index_opclass_options(Relation indrel, AttrNumber attnum, Datum attoptions,
912
            bool validate)
913
0
{
914
0
  int     amoptsprocnum = indrel->rd_indam->amoptsprocnum;
915
0
  Oid     procid = InvalidOid;
916
0
  FmgrInfo   *procinfo;
917
0
  local_relopts relopts;
918
919
  /* fetch options support procedure if specified */
920
0
  if (amoptsprocnum != 0)
921
0
    procid = index_getprocid(indrel, attnum, amoptsprocnum);
922
923
0
  if (!OidIsValid(procid))
924
0
  {
925
0
    Oid     opclass;
926
0
    Datum   indclassDatum;
927
0
    oidvector  *indclass;
928
929
0
    if (!DatumGetPointer(attoptions))
930
0
      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
0
    indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indrel->rd_indextuple,
937
0
                         Anum_pg_index_indclass);
938
0
    indclass = (oidvector *) DatumGetPointer(indclassDatum);
939
0
    opclass = indclass->values[attnum - 1];
940
941
0
    ereport(ERROR,
942
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
943
0
         errmsg("operator class %s has no options",
944
0
            generate_opclass_name(opclass))));
945
0
  }
946
947
0
  init_local_reloptions(&relopts, 0);
948
949
0
  procinfo = index_getprocinfo(indrel, attnum, amoptsprocnum);
950
951
0
  (void) FunctionCall1(procinfo, PointerGetDatum(&relopts));
952
953
0
  return build_local_reloptions(&relopts, attoptions, validate);
954
0
}