1 /*-------------------------------------------------------------------------
2  *
3  * indexam.c
4  *	  general index access method routines
5  *
6  * Portions Copyright (c) 1996-2016, 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_getnext_tid	- get the next TID from a scan
24  *		index_fetch_heap		- get the scan's next heap tuple
25  *		index_getnext	- get the next heap tuple from a scan
26  *		index_getbitmap - get all tuples from a scan
27  *		index_bulk_delete	- bulk deletion of index tuples
28  *		index_vacuum_cleanup	- post-deletion cleanup of an index
29  *		index_can_return	- does index support index-only scans?
30  *		index_getprocid - get a support procedure OID
31  *		index_getprocinfo - get a support procedure's lookup info
32  *
33  * NOTES
34  *		This file contains the index_ routines which used
35  *		to be a scattered collection of stuff in access/genam.
36  *
37  *
38  * old comments
39  *		Scans are implemented as follows:
40  *
41  *		`0' represents an invalid item pointer.
42  *		`-' represents an unknown item pointer.
43  *		`X' represents a known item pointers.
44  *		`+' represents known or invalid item pointers.
45  *		`*' represents any item pointers.
46  *
47  *		State is represented by a triple of these symbols in the order of
48  *		previous, current, next.  Note that the case of reverse scans works
49  *		identically.
50  *
51  *				State	Result
52  *		(1)		+ + -	+ 0 0			(if the next item pointer is invalid)
53  *		(2)				+ X -			(otherwise)
54  *		(3)		* 0 0	* 0 0			(no change)
55  *		(4)		+ X 0	X 0 0			(shift)
56  *		(5)		* + X	+ X -			(shift, add unknown)
57  *
58  *		All other states cannot occur.
59  *
60  *		Note: It would be possible to cache the status of the previous and
61  *			  next item pointer using the flags.
62  *
63  *-------------------------------------------------------------------------
64  */
65 
66 #include "postgres.h"
67 
68 #include "access/amapi.h"
69 #include "access/relscan.h"
70 #include "access/transam.h"
71 #include "access/xlog.h"
72 #include "catalog/catalog.h"
73 #include "catalog/index.h"
74 #include "pgstat.h"
75 #include "storage/bufmgr.h"
76 #include "storage/lmgr.h"
77 #include "storage/predicate.h"
78 #include "utils/snapmgr.h"
79 #include "utils/tqual.h"
80 
81 
82 /* ----------------------------------------------------------------
83  *					macros used in index_ routines
84  *
85  * Note: the ReindexIsProcessingIndex() check in RELATION_CHECKS is there
86  * to check that we don't try to scan or do retail insertions into an index
87  * that is currently being rebuilt or pending rebuild.  This helps to catch
88  * things that don't work when reindexing system catalogs.  The assertion
89  * doesn't prevent the actual rebuild because we don't use RELATION_CHECKS
90  * when calling the index AM's ambuild routine, and there is no reason for
91  * ambuild to call its subsidiary routines through this file.
92  * ----------------------------------------------------------------
93  */
94 #define RELATION_CHECKS \
95 ( \
96 	AssertMacro(RelationIsValid(indexRelation)), \
97 	AssertMacro(PointerIsValid(indexRelation->rd_amroutine)), \
98 	AssertMacro(!ReindexIsProcessingIndex(RelationGetRelid(indexRelation))) \
99 )
100 
101 #define SCAN_CHECKS \
102 ( \
103 	AssertMacro(IndexScanIsValid(scan)), \
104 	AssertMacro(RelationIsValid(scan->indexRelation)), \
105 	AssertMacro(PointerIsValid(scan->indexRelation->rd_amroutine)) \
106 )
107 
108 #define CHECK_REL_PROCEDURE(pname) \
109 do { \
110 	if (indexRelation->rd_amroutine->pname == NULL) \
111 		elog(ERROR, "function %s is not defined for index %s", \
112 			 CppAsString(pname), RelationGetRelationName(indexRelation)); \
113 } while(0)
114 
115 #define CHECK_SCAN_PROCEDURE(pname) \
116 do { \
117 	if (scan->indexRelation->rd_amroutine->pname == NULL) \
118 		elog(ERROR, "function %s is not defined for index %s", \
119 			 CppAsString(pname), RelationGetRelationName(scan->indexRelation)); \
120 } while(0)
121 
122 static IndexScanDesc index_beginscan_internal(Relation indexRelation,
123 						 int nkeys, int norderbys, Snapshot snapshot);
124 
125 
126 /* ----------------------------------------------------------------
127  *				   index_ interface functions
128  * ----------------------------------------------------------------
129  */
130 
131 /* ----------------
132  *		index_open - open an index relation by relation OID
133  *
134  *		If lockmode is not "NoLock", the specified kind of lock is
135  *		obtained on the index.  (Generally, NoLock should only be
136  *		used if the caller knows it has some appropriate lock on the
137  *		index already.)
138  *
139  *		An error is raised if the index does not exist.
140  *
141  *		This is a convenience routine adapted for indexscan use.
142  *		Some callers may prefer to use relation_open directly.
143  * ----------------
144  */
145 Relation
index_open(Oid relationId,LOCKMODE lockmode)146 index_open(Oid relationId, LOCKMODE lockmode)
147 {
148 	Relation	r;
149 
150 	r = relation_open(relationId, lockmode);
151 
152 	if (r->rd_rel->relkind != RELKIND_INDEX)
153 		ereport(ERROR,
154 				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
155 				 errmsg("\"%s\" is not an index",
156 						RelationGetRelationName(r))));
157 
158 	return r;
159 }
160 
161 /* ----------------
162  *		index_close - close an index relation
163  *
164  *		If lockmode is not "NoLock", we then release the specified lock.
165  *
166  *		Note that it is often sensible to hold a lock beyond index_close;
167  *		in that case, the lock is released automatically at xact end.
168  * ----------------
169  */
170 void
index_close(Relation relation,LOCKMODE lockmode)171 index_close(Relation relation, LOCKMODE lockmode)
172 {
173 	LockRelId	relid = relation->rd_lockInfo.lockRelId;
174 
175 	Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
176 
177 	/* The relcache does the real work... */
178 	RelationClose(relation);
179 
180 	if (lockmode != NoLock)
181 		UnlockRelationId(&relid, lockmode);
182 }
183 
184 /* ----------------
185  *		index_insert - insert an index tuple into a relation
186  * ----------------
187  */
188 bool
index_insert(Relation indexRelation,Datum * values,bool * isnull,ItemPointer heap_t_ctid,Relation heapRelation,IndexUniqueCheck checkUnique)189 index_insert(Relation indexRelation,
190 			 Datum *values,
191 			 bool *isnull,
192 			 ItemPointer heap_t_ctid,
193 			 Relation heapRelation,
194 			 IndexUniqueCheck checkUnique)
195 {
196 	RELATION_CHECKS;
197 	CHECK_REL_PROCEDURE(aminsert);
198 
199 	if (!(indexRelation->rd_amroutine->ampredlocks))
200 		CheckForSerializableConflictIn(indexRelation,
201 									   (HeapTuple) NULL,
202 									   InvalidBuffer);
203 
204 	return indexRelation->rd_amroutine->aminsert(indexRelation, values, isnull,
205 												 heap_t_ctid, heapRelation,
206 												 checkUnique);
207 }
208 
209 /*
210  * index_beginscan - start a scan of an index with amgettuple
211  *
212  * Caller must be holding suitable locks on the heap and the index.
213  */
214 IndexScanDesc
index_beginscan(Relation heapRelation,Relation indexRelation,Snapshot snapshot,int nkeys,int norderbys)215 index_beginscan(Relation heapRelation,
216 				Relation indexRelation,
217 				Snapshot snapshot,
218 				int nkeys, int norderbys)
219 {
220 	IndexScanDesc scan;
221 
222 	scan = index_beginscan_internal(indexRelation, nkeys, norderbys, snapshot);
223 
224 	/*
225 	 * Save additional parameters into the scandesc.  Everything else was set
226 	 * up by RelationGetIndexScan.
227 	 */
228 	scan->heapRelation = heapRelation;
229 	scan->xs_snapshot = snapshot;
230 
231 	return scan;
232 }
233 
234 /*
235  * index_beginscan_bitmap - start a scan of an index with amgetbitmap
236  *
237  * As above, caller had better be holding some lock on the parent heap
238  * relation, even though it's not explicitly mentioned here.
239  */
240 IndexScanDesc
index_beginscan_bitmap(Relation indexRelation,Snapshot snapshot,int nkeys)241 index_beginscan_bitmap(Relation indexRelation,
242 					   Snapshot snapshot,
243 					   int nkeys)
244 {
245 	IndexScanDesc scan;
246 
247 	scan = index_beginscan_internal(indexRelation, nkeys, 0, snapshot);
248 
249 	/*
250 	 * Save additional parameters into the scandesc.  Everything else was set
251 	 * up by RelationGetIndexScan.
252 	 */
253 	scan->xs_snapshot = snapshot;
254 
255 	return scan;
256 }
257 
258 /*
259  * index_beginscan_internal --- common code for index_beginscan variants
260  */
261 static IndexScanDesc
index_beginscan_internal(Relation indexRelation,int nkeys,int norderbys,Snapshot snapshot)262 index_beginscan_internal(Relation indexRelation,
263 						 int nkeys, int norderbys, Snapshot snapshot)
264 {
265 	RELATION_CHECKS;
266 	CHECK_REL_PROCEDURE(ambeginscan);
267 
268 	if (!(indexRelation->rd_amroutine->ampredlocks))
269 		PredicateLockRelation(indexRelation, snapshot);
270 
271 	/*
272 	 * We hold a reference count to the relcache entry throughout the scan.
273 	 */
274 	RelationIncrementReferenceCount(indexRelation);
275 
276 	/*
277 	 * Tell the AM to open a scan.
278 	 */
279 	return indexRelation->rd_amroutine->ambeginscan(indexRelation, nkeys,
280 													norderbys);
281 }
282 
283 /* ----------------
284  *		index_rescan  - (re)start a scan of an index
285  *
286  * During a restart, the caller may specify a new set of scankeys and/or
287  * orderbykeys; but the number of keys cannot differ from what index_beginscan
288  * was told.  (Later we might relax that to "must not exceed", but currently
289  * the index AMs tend to assume that scan->numberOfKeys is what to believe.)
290  * To restart the scan without changing keys, pass NULL for the key arrays.
291  * (Of course, keys *must* be passed on the first call, unless
292  * scan->numberOfKeys is zero.)
293  * ----------------
294  */
295 void
index_rescan(IndexScanDesc scan,ScanKey keys,int nkeys,ScanKey orderbys,int norderbys)296 index_rescan(IndexScanDesc scan,
297 			 ScanKey keys, int nkeys,
298 			 ScanKey orderbys, int norderbys)
299 {
300 	SCAN_CHECKS;
301 	CHECK_SCAN_PROCEDURE(amrescan);
302 
303 	Assert(nkeys == scan->numberOfKeys);
304 	Assert(norderbys == scan->numberOfOrderBys);
305 
306 	/* Release any held pin on a heap page */
307 	if (BufferIsValid(scan->xs_cbuf))
308 	{
309 		ReleaseBuffer(scan->xs_cbuf);
310 		scan->xs_cbuf = InvalidBuffer;
311 	}
312 
313 	scan->xs_continue_hot = false;
314 
315 	scan->kill_prior_tuple = false;		/* for safety */
316 
317 	scan->indexRelation->rd_amroutine->amrescan(scan, keys, nkeys,
318 												orderbys, norderbys);
319 }
320 
321 /* ----------------
322  *		index_endscan - end a scan
323  * ----------------
324  */
325 void
index_endscan(IndexScanDesc scan)326 index_endscan(IndexScanDesc scan)
327 {
328 	SCAN_CHECKS;
329 	CHECK_SCAN_PROCEDURE(amendscan);
330 
331 	/* Release any held pin on a heap page */
332 	if (BufferIsValid(scan->xs_cbuf))
333 	{
334 		ReleaseBuffer(scan->xs_cbuf);
335 		scan->xs_cbuf = InvalidBuffer;
336 	}
337 
338 	/* End the AM's scan */
339 	scan->indexRelation->rd_amroutine->amendscan(scan);
340 
341 	/* Release index refcount acquired by index_beginscan */
342 	RelationDecrementReferenceCount(scan->indexRelation);
343 
344 	/* Release the scan data structure itself */
345 	IndexScanEnd(scan);
346 }
347 
348 /* ----------------
349  *		index_markpos  - mark a scan position
350  * ----------------
351  */
352 void
index_markpos(IndexScanDesc scan)353 index_markpos(IndexScanDesc scan)
354 {
355 	SCAN_CHECKS;
356 	CHECK_SCAN_PROCEDURE(ammarkpos);
357 
358 	scan->indexRelation->rd_amroutine->ammarkpos(scan);
359 }
360 
361 /* ----------------
362  *		index_restrpos	- restore a scan position
363  *
364  * NOTE: this only restores the internal scan state of the index AM.
365  * The current result tuple (scan->xs_ctup) doesn't change.  See comments
366  * for ExecRestrPos().
367  *
368  * NOTE: in the presence of HOT chains, mark/restore only works correctly
369  * if the scan's snapshot is MVCC-safe; that ensures that there's at most one
370  * returnable tuple in each HOT chain, and so restoring the prior state at the
371  * granularity of the index AM is sufficient.  Since the only current user
372  * of mark/restore functionality is nodeMergejoin.c, this effectively means
373  * that merge-join plans only work for MVCC snapshots.  This could be fixed
374  * if necessary, but for now it seems unimportant.
375  * ----------------
376  */
377 void
index_restrpos(IndexScanDesc scan)378 index_restrpos(IndexScanDesc scan)
379 {
380 	Assert(IsMVCCSnapshot(scan->xs_snapshot));
381 
382 	SCAN_CHECKS;
383 	CHECK_SCAN_PROCEDURE(amrestrpos);
384 
385 	scan->xs_continue_hot = false;
386 
387 	scan->kill_prior_tuple = false;		/* for safety */
388 
389 	scan->indexRelation->rd_amroutine->amrestrpos(scan);
390 }
391 
392 /* ----------------
393  * index_getnext_tid - get the next TID from a scan
394  *
395  * The result is the next TID satisfying the scan keys,
396  * or NULL if no more matching tuples exist.
397  * ----------------
398  */
399 ItemPointer
index_getnext_tid(IndexScanDesc scan,ScanDirection direction)400 index_getnext_tid(IndexScanDesc scan, ScanDirection direction)
401 {
402 	bool		found;
403 
404 	SCAN_CHECKS;
405 	CHECK_SCAN_PROCEDURE(amgettuple);
406 
407 	Assert(TransactionIdIsValid(RecentGlobalXmin));
408 
409 	/*
410 	 * The AM's amgettuple proc finds the next index entry matching the scan
411 	 * keys, and puts the TID into scan->xs_ctup.t_self.  It should also set
412 	 * scan->xs_recheck and possibly scan->xs_itup, though we pay no attention
413 	 * to those fields here.
414 	 */
415 	found = scan->indexRelation->rd_amroutine->amgettuple(scan, direction);
416 
417 	/* Reset kill flag immediately for safety */
418 	scan->kill_prior_tuple = false;
419 
420 	/* If we're out of index entries, we're done */
421 	if (!found)
422 	{
423 		/* ... but first, release any held pin on a heap page */
424 		if (BufferIsValid(scan->xs_cbuf))
425 		{
426 			ReleaseBuffer(scan->xs_cbuf);
427 			scan->xs_cbuf = InvalidBuffer;
428 		}
429 		return NULL;
430 	}
431 
432 	pgstat_count_index_tuples(scan->indexRelation, 1);
433 
434 	/* Return the TID of the tuple we found. */
435 	return &scan->xs_ctup.t_self;
436 }
437 
438 /* ----------------
439  *		index_fetch_heap - get the scan's next heap tuple
440  *
441  * The result is a visible heap tuple associated with the index TID most
442  * recently fetched by index_getnext_tid, or NULL if no more matching tuples
443  * exist.  (There can be more than one matching tuple because of HOT chains,
444  * although when using an MVCC snapshot it should be impossible for more than
445  * one such tuple to exist.)
446  *
447  * On success, the buffer containing the heap tup is pinned (the pin will be
448  * dropped in a future index_getnext_tid, index_fetch_heap or index_endscan
449  * call).
450  *
451  * Note: caller must check scan->xs_recheck, and perform rechecking of the
452  * scan keys if required.  We do not do that here because we don't have
453  * enough information to do it efficiently in the general case.
454  * ----------------
455  */
456 HeapTuple
index_fetch_heap(IndexScanDesc scan)457 index_fetch_heap(IndexScanDesc scan)
458 {
459 	ItemPointer tid = &scan->xs_ctup.t_self;
460 	bool		all_dead = false;
461 	bool		got_heap_tuple;
462 
463 	/* We can skip the buffer-switching logic if we're in mid-HOT chain. */
464 	if (!scan->xs_continue_hot)
465 	{
466 		/* Switch to correct buffer if we don't have it already */
467 		Buffer		prev_buf = scan->xs_cbuf;
468 
469 		scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf,
470 											 scan->heapRelation,
471 											 ItemPointerGetBlockNumber(tid));
472 
473 		/*
474 		 * Prune page, but only if we weren't already on this page
475 		 */
476 		if (prev_buf != scan->xs_cbuf)
477 			heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf);
478 	}
479 
480 	/* Obtain share-lock on the buffer so we can examine visibility */
481 	LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
482 	got_heap_tuple = heap_hot_search_buffer(tid, scan->heapRelation,
483 											scan->xs_cbuf,
484 											scan->xs_snapshot,
485 											&scan->xs_ctup,
486 											&all_dead,
487 											!scan->xs_continue_hot);
488 	LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
489 
490 	if (got_heap_tuple)
491 	{
492 		/*
493 		 * Only in a non-MVCC snapshot can more than one member of the HOT
494 		 * chain be visible.
495 		 */
496 		scan->xs_continue_hot = !IsMVCCSnapshot(scan->xs_snapshot);
497 		pgstat_count_heap_fetch(scan->indexRelation);
498 		return &scan->xs_ctup;
499 	}
500 
501 	/* We've reached the end of the HOT chain. */
502 	scan->xs_continue_hot = false;
503 
504 	/*
505 	 * If we scanned a whole HOT chain and found only dead tuples, tell index
506 	 * AM to kill its entry for that TID (this will take effect in the next
507 	 * amgettuple call, in index_getnext_tid).  We do not do this when in
508 	 * recovery because it may violate MVCC to do so.  See comments in
509 	 * RelationGetIndexScan().
510 	 */
511 	if (!scan->xactStartedInRecovery)
512 		scan->kill_prior_tuple = all_dead;
513 
514 	return NULL;
515 }
516 
517 /* ----------------
518  *		index_getnext - get the next heap tuple from a scan
519  *
520  * The result is the next heap tuple satisfying the scan keys and the
521  * snapshot, or NULL if no more matching tuples exist.
522  *
523  * On success, the buffer containing the heap tup is pinned (the pin will be
524  * dropped in a future index_getnext_tid, index_fetch_heap or index_endscan
525  * call).
526  *
527  * Note: caller must check scan->xs_recheck, and perform rechecking of the
528  * scan keys if required.  We do not do that here because we don't have
529  * enough information to do it efficiently in the general case.
530  * ----------------
531  */
532 HeapTuple
index_getnext(IndexScanDesc scan,ScanDirection direction)533 index_getnext(IndexScanDesc scan, ScanDirection direction)
534 {
535 	HeapTuple	heapTuple;
536 	ItemPointer tid;
537 
538 	for (;;)
539 	{
540 		if (scan->xs_continue_hot)
541 		{
542 			/*
543 			 * We are resuming scan of a HOT chain after having returned an
544 			 * earlier member.  Must still hold pin on current heap page.
545 			 */
546 			Assert(BufferIsValid(scan->xs_cbuf));
547 			Assert(ItemPointerGetBlockNumber(&scan->xs_ctup.t_self) ==
548 				   BufferGetBlockNumber(scan->xs_cbuf));
549 		}
550 		else
551 		{
552 			/* Time to fetch the next TID from the index */
553 			tid = index_getnext_tid(scan, direction);
554 
555 			/* If we're out of index entries, we're done */
556 			if (tid == NULL)
557 				break;
558 		}
559 
560 		/*
561 		 * Fetch the next (or only) visible heap tuple for this index entry.
562 		 * If we don't find anything, loop around and grab the next TID from
563 		 * the index.
564 		 */
565 		heapTuple = index_fetch_heap(scan);
566 		if (heapTuple != NULL)
567 			return heapTuple;
568 	}
569 
570 	return NULL;				/* failure exit */
571 }
572 
573 /* ----------------
574  *		index_getbitmap - get all tuples at once from an index scan
575  *
576  * Adds the TIDs of all heap tuples satisfying the scan keys to a bitmap.
577  * Since there's no interlock between the index scan and the eventual heap
578  * access, this is only safe to use with MVCC-based snapshots: the heap
579  * item slot could have been replaced by a newer tuple by the time we get
580  * to it.
581  *
582  * Returns the number of matching tuples found.  (Note: this might be only
583  * approximate, so it should only be used for statistical purposes.)
584  * ----------------
585  */
586 int64
index_getbitmap(IndexScanDesc scan,TIDBitmap * bitmap)587 index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap)
588 {
589 	int64		ntids;
590 
591 	SCAN_CHECKS;
592 	CHECK_SCAN_PROCEDURE(amgetbitmap);
593 
594 	/* just make sure this is false... */
595 	scan->kill_prior_tuple = false;
596 
597 	/*
598 	 * have the am's getbitmap proc do all the work.
599 	 */
600 	ntids = scan->indexRelation->rd_amroutine->amgetbitmap(scan, bitmap);
601 
602 	pgstat_count_index_tuples(scan->indexRelation, ntids);
603 
604 	return ntids;
605 }
606 
607 /* ----------------
608  *		index_bulk_delete - do mass deletion of index entries
609  *
610  *		callback routine tells whether a given main-heap tuple is
611  *		to be deleted
612  *
613  *		return value is an optional palloc'd struct of statistics
614  * ----------------
615  */
616 IndexBulkDeleteResult *
index_bulk_delete(IndexVacuumInfo * info,IndexBulkDeleteResult * stats,IndexBulkDeleteCallback callback,void * callback_state)617 index_bulk_delete(IndexVacuumInfo *info,
618 				  IndexBulkDeleteResult *stats,
619 				  IndexBulkDeleteCallback callback,
620 				  void *callback_state)
621 {
622 	Relation	indexRelation = info->index;
623 
624 	RELATION_CHECKS;
625 	CHECK_REL_PROCEDURE(ambulkdelete);
626 
627 	return indexRelation->rd_amroutine->ambulkdelete(info, stats,
628 												   callback, callback_state);
629 }
630 
631 /* ----------------
632  *		index_vacuum_cleanup - do post-deletion cleanup of an index
633  *
634  *		return value is an optional palloc'd struct of statistics
635  * ----------------
636  */
637 IndexBulkDeleteResult *
index_vacuum_cleanup(IndexVacuumInfo * info,IndexBulkDeleteResult * stats)638 index_vacuum_cleanup(IndexVacuumInfo *info,
639 					 IndexBulkDeleteResult *stats)
640 {
641 	Relation	indexRelation = info->index;
642 
643 	RELATION_CHECKS;
644 	CHECK_REL_PROCEDURE(amvacuumcleanup);
645 
646 	return indexRelation->rd_amroutine->amvacuumcleanup(info, stats);
647 }
648 
649 /* ----------------
650  *		index_can_return
651  *
652  *		Does the index access method support index-only scans for the given
653  *		column?
654  * ----------------
655  */
656 bool
index_can_return(Relation indexRelation,int attno)657 index_can_return(Relation indexRelation, int attno)
658 {
659 	RELATION_CHECKS;
660 
661 	/* amcanreturn is optional; assume FALSE if not provided by AM */
662 	if (indexRelation->rd_amroutine->amcanreturn == NULL)
663 		return false;
664 
665 	return indexRelation->rd_amroutine->amcanreturn(indexRelation, attno);
666 }
667 
668 /* ----------------
669  *		index_getprocid
670  *
671  *		Index access methods typically require support routines that are
672  *		not directly the implementation of any WHERE-clause query operator
673  *		and so cannot be kept in pg_amop.  Instead, such routines are kept
674  *		in pg_amproc.  These registered procedure OIDs are assigned numbers
675  *		according to a convention established by the access method.
676  *		The general index code doesn't know anything about the routines
677  *		involved; it just builds an ordered list of them for
678  *		each attribute on which an index is defined.
679  *
680  *		As of Postgres 8.3, support routines within an operator family
681  *		are further subdivided by the "left type" and "right type" of the
682  *		query operator(s) that they support.  The "default" functions for a
683  *		particular indexed attribute are those with both types equal to
684  *		the index opclass' opcintype (note that this is subtly different
685  *		from the indexed attribute's own type: it may be a binary-compatible
686  *		type instead).  Only the default functions are stored in relcache
687  *		entries --- access methods can use the syscache to look up non-default
688  *		functions.
689  *
690  *		This routine returns the requested default procedure OID for a
691  *		particular indexed attribute.
692  * ----------------
693  */
694 RegProcedure
index_getprocid(Relation irel,AttrNumber attnum,uint16 procnum)695 index_getprocid(Relation irel,
696 				AttrNumber attnum,
697 				uint16 procnum)
698 {
699 	RegProcedure *loc;
700 	int			nproc;
701 	int			procindex;
702 
703 	nproc = irel->rd_amroutine->amsupport;
704 
705 	Assert(procnum > 0 && procnum <= (uint16) nproc);
706 
707 	procindex = (nproc * (attnum - 1)) + (procnum - 1);
708 
709 	loc = irel->rd_support;
710 
711 	Assert(loc != NULL);
712 
713 	return loc[procindex];
714 }
715 
716 /* ----------------
717  *		index_getprocinfo
718  *
719  *		This routine allows index AMs to keep fmgr lookup info for
720  *		support procs in the relcache.  As above, only the "default"
721  *		functions for any particular indexed attribute are cached.
722  *
723  * Note: the return value points into cached data that will be lost during
724  * any relcache rebuild!  Therefore, either use the callinfo right away,
725  * or save it only after having acquired some type of lock on the index rel.
726  * ----------------
727  */
728 FmgrInfo *
index_getprocinfo(Relation irel,AttrNumber attnum,uint16 procnum)729 index_getprocinfo(Relation irel,
730 				  AttrNumber attnum,
731 				  uint16 procnum)
732 {
733 	FmgrInfo   *locinfo;
734 	int			nproc;
735 	int			procindex;
736 
737 	nproc = irel->rd_amroutine->amsupport;
738 
739 	Assert(procnum > 0 && procnum <= (uint16) nproc);
740 
741 	procindex = (nproc * (attnum - 1)) + (procnum - 1);
742 
743 	locinfo = irel->rd_supportinfo;
744 
745 	Assert(locinfo != NULL);
746 
747 	locinfo += procindex;
748 
749 	/* Initialize the lookup info if first time through */
750 	if (locinfo->fn_oid == InvalidOid)
751 	{
752 		RegProcedure *loc = irel->rd_support;
753 		RegProcedure procId;
754 
755 		Assert(loc != NULL);
756 
757 		procId = loc[procindex];
758 
759 		/*
760 		 * Complain if function was not found during IndexSupportInitialize.
761 		 * This should not happen unless the system tables contain bogus
762 		 * entries for the index opclass.  (If an AM wants to allow a support
763 		 * function to be optional, it can use index_getprocid.)
764 		 */
765 		if (!RegProcedureIsValid(procId))
766 			elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
767 				 procnum, attnum, RelationGetRelationName(irel));
768 
769 		fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
770 	}
771 
772 	return locinfo;
773 }
774