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