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 : : }
|