]> granicus.if.org Git - postgresql/blob - src/backend/access/index/indexam.c
Revise pgstats stuff to fix the problems with not counting accesses
[postgresql] / src / backend / access / index / indexam.c
1 /*-------------------------------------------------------------------------
2  *
3  * indexam.c
4  *        general index access method routines
5  *
6  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/access/index/indexam.c,v 1.85 2005/10/06 02:29:11 tgl Exp $
12  *
13  * INTERFACE ROUTINES
14  *              index_open              - open an index relation by relation OID
15  *              index_openrv    - open an index relation specified by a RangeVar
16  *              index_close             - close an index relation
17  *              index_beginscan - start a scan of an index with amgettuple
18  *              index_beginscan_multi - start a scan of an index with amgetmulti
19  *              index_rescan    - restart a scan of an index
20  *              index_endscan   - end a scan
21  *              index_insert    - insert an index tuple into a relation
22  *              index_markpos   - mark a scan position
23  *              index_restrpos  - restore a scan position
24  *              index_getnext   - get the next tuple from a scan
25  *              index_getmulti  - get multiple tuples from a scan
26  *              index_bulk_delete       - bulk deletion of index tuples
27  *              index_vacuum_cleanup    - post-deletion cleanup of an index
28  *              index_getprocid - get a support procedure OID
29  *              index_getprocinfo - get a support procedure's lookup info
30  *
31  * NOTES
32  *              This file contains the index_ routines which used
33  *              to be a scattered collection of stuff in access/genam.
34  *
35  *
36  * old comments
37  *              Scans are implemented as follows:
38  *
39  *              `0' represents an invalid item pointer.
40  *              `-' represents an unknown item pointer.
41  *              `X' represents a known item pointers.
42  *              `+' represents known or invalid item pointers.
43  *              `*' represents any item pointers.
44  *
45  *              State is represented by a triple of these symbols in the order of
46  *              previous, current, next.  Note that the case of reverse scans works
47  *              identically.
48  *
49  *                              State   Result
50  *              (1)             + + -   + 0 0                   (if the next item pointer is invalid)
51  *              (2)                             + X -                   (otherwise)
52  *              (3)             * 0 0   * 0 0                   (no change)
53  *              (4)             + X 0   X 0 0                   (shift)
54  *              (5)             * + X   + X -                   (shift, add unknown)
55  *
56  *              All other states cannot occur.
57  *
58  *              Note: It would be possible to cache the status of the previous and
59  *                        next item pointer using the flags.
60  *
61  *-------------------------------------------------------------------------
62  */
63
64 #include "postgres.h"
65
66 #include "access/genam.h"
67 #include "access/heapam.h"
68 #include "pgstat.h"
69 #include "utils/relcache.h"
70
71
72 /* ----------------------------------------------------------------
73  *                                      macros used in index_ routines
74  * ----------------------------------------------------------------
75  */
76 #define RELATION_CHECKS \
77 ( \
78         AssertMacro(RelationIsValid(indexRelation)), \
79         AssertMacro(PointerIsValid(indexRelation->rd_am)) \
80 )
81
82 #define SCAN_CHECKS \
83 ( \
84         AssertMacro(IndexScanIsValid(scan)), \
85         AssertMacro(RelationIsValid(scan->indexRelation)), \
86         AssertMacro(PointerIsValid(scan->indexRelation->rd_am)) \
87 )
88
89 #define GET_REL_PROCEDURE(pname) \
90 do { \
91         procedure = &indexRelation->rd_aminfo->pname; \
92         if (!OidIsValid(procedure->fn_oid)) \
93         { \
94                 RegProcedure    procOid = indexRelation->rd_am->pname; \
95                 if (!RegProcedureIsValid(procOid)) \
96                         elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
97                 fmgr_info_cxt(procOid, procedure, indexRelation->rd_indexcxt); \
98         } \
99 } while(0)
100
101 #define GET_SCAN_PROCEDURE(pname) \
102 do { \
103         procedure = &scan->indexRelation->rd_aminfo->pname; \
104         if (!OidIsValid(procedure->fn_oid)) \
105         { \
106                 RegProcedure    procOid = scan->indexRelation->rd_am->pname; \
107                 if (!RegProcedureIsValid(procOid)) \
108                         elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
109                 fmgr_info_cxt(procOid, procedure, scan->indexRelation->rd_indexcxt); \
110         } \
111 } while(0)
112
113 static IndexScanDesc index_beginscan_internal(Relation indexRelation,
114                                                                                           int nkeys, ScanKey key);
115
116
117 /* ----------------------------------------------------------------
118  *                                 index_ interface functions
119  * ----------------------------------------------------------------
120  */
121
122 /* ----------------
123  *              index_open - open an index relation by relation OID
124  *
125  *              Note: we acquire no lock on the index.  A lock is not needed when
126  *              simply examining the index reldesc; the index's schema information
127  *              is considered to be protected by the lock that the caller had better
128  *              be holding on the parent relation.  Some type of lock should be
129  *              obtained on the index before physically accessing it, however.
130  *              This is handled automatically for most uses by index_beginscan
131  *              and index_endscan for scan cases, or by ExecOpenIndices and
132  *              ExecCloseIndices for update cases.  Other callers will need to
133  *              obtain their own locks.
134  *
135  *              This is a convenience routine adapted for indexscan use.
136  *              Some callers may prefer to use relation_open directly.
137  * ----------------
138  */
139 Relation
140 index_open(Oid relationId)
141 {
142         Relation        r;
143
144         r = relation_open(relationId, NoLock);
145
146         if (r->rd_rel->relkind != RELKIND_INDEX)
147                 ereport(ERROR,
148                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
149                                  errmsg("\"%s\" is not an index",
150                                                 RelationGetRelationName(r))));
151
152         pgstat_initstats(&r->pgstat_info, r);
153
154         return r;
155 }
156
157 /* ----------------
158  *              index_openrv - open an index relation specified
159  *              by a RangeVar node
160  *
161  *              As above, but relation is specified by a RangeVar.
162  * ----------------
163  */
164 Relation
165 index_openrv(const RangeVar *relation)
166 {
167         Relation        r;
168
169         r = relation_openrv(relation, NoLock);
170
171         if (r->rd_rel->relkind != RELKIND_INDEX)
172                 ereport(ERROR,
173                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
174                                  errmsg("\"%s\" is not an index",
175                                                 RelationGetRelationName(r))));
176
177         pgstat_initstats(&r->pgstat_info, r);
178
179         return r;
180 }
181
182 /* ----------------
183  *              index_close - close a index relation
184  *
185  *              presently the relcache routines do all the work we need
186  *              to open/close index relations.
187  * ----------------
188  */
189 void
190 index_close(Relation relation)
191 {
192         RelationClose(relation);
193 }
194
195 /* ----------------
196  *              index_insert - insert an index tuple into a relation
197  * ----------------
198  */
199 bool
200 index_insert(Relation indexRelation,
201                          Datum *values,
202                          bool *isnull,
203                          ItemPointer heap_t_ctid,
204                          Relation heapRelation,
205                          bool check_uniqueness)
206 {
207         FmgrInfo   *procedure;
208
209         RELATION_CHECKS;
210         GET_REL_PROCEDURE(aminsert);
211
212         /*
213          * have the am's insert proc do all the work.
214          */
215         return DatumGetBool(FunctionCall6(procedure,
216                                                                           PointerGetDatum(indexRelation),
217                                                                           PointerGetDatum(values),
218                                                                           PointerGetDatum(isnull),
219                                                                           PointerGetDatum(heap_t_ctid),
220                                                                           PointerGetDatum(heapRelation),
221                                                                           BoolGetDatum(check_uniqueness)));
222 }
223
224 /*
225  * index_beginscan - start a scan of an index with amgettuple
226  *
227  * Note: heapRelation may be NULL if there is no intention of calling
228  * index_getnext on this scan; index_getnext_indexitem will not use the
229  * heapRelation link (nor the snapshot).  However, the caller had better
230  * be holding some kind of lock on the heap relation in any case, to ensure
231  * no one deletes it (or the index) out from under us.
232  */
233 IndexScanDesc
234 index_beginscan(Relation heapRelation,
235                                 Relation indexRelation,
236                                 Snapshot snapshot,
237                                 int nkeys, ScanKey key)
238 {
239         IndexScanDesc scan;
240
241         scan = index_beginscan_internal(indexRelation, nkeys, key);
242
243         /*
244          * Save additional parameters into the scandesc.  Everything else was
245          * set up by RelationGetIndexScan.
246          */
247         scan->is_multiscan = false;
248         scan->heapRelation = heapRelation;
249         scan->xs_snapshot = snapshot;
250
251         return scan;
252 }
253
254 /*
255  * index_beginscan_multi - start a scan of an index with amgetmulti
256  *
257  * As above, caller had better be holding some lock on the parent heap
258  * relation, even though it's not explicitly mentioned here.
259  */
260 IndexScanDesc
261 index_beginscan_multi(Relation indexRelation,
262                                           Snapshot snapshot,
263                                           int nkeys, ScanKey key)
264 {
265         IndexScanDesc scan;
266
267         scan = index_beginscan_internal(indexRelation, nkeys, key);
268
269         /*
270          * Save additional parameters into the scandesc.  Everything else was
271          * set up by RelationGetIndexScan.
272          */
273         scan->is_multiscan = true;
274         scan->xs_snapshot = snapshot;
275
276         return scan;
277 }
278
279 /*
280  * index_beginscan_internal --- common code for index_beginscan variants
281  */
282 static IndexScanDesc
283 index_beginscan_internal(Relation indexRelation,
284                                                  int nkeys, ScanKey key)
285 {
286         IndexScanDesc scan;
287         FmgrInfo   *procedure;
288
289         RELATION_CHECKS;
290
291         RelationIncrementReferenceCount(indexRelation);
292
293         /*
294          * Acquire AccessShareLock for the duration of the scan
295          *
296          * Note: we could get an SI inval message here and consequently have to
297          * rebuild the relcache entry.  The refcount increment above ensures
298          * that we will rebuild it and not just flush it...
299          */
300         LockRelation(indexRelation, AccessShareLock);
301
302         /*
303          * LockRelation can clean rd_aminfo structure, so fill procedure
304          * after LockRelation 
305          */
306
307         GET_REL_PROCEDURE(ambeginscan);
308
309         /*
310          * Tell the AM to open a scan.
311          */
312         scan = (IndexScanDesc)
313                 DatumGetPointer(FunctionCall3(procedure,
314                                                                           PointerGetDatum(indexRelation),
315                                                                           Int32GetDatum(nkeys),
316                                                                           PointerGetDatum(key)));
317
318         return scan;
319 }
320
321 /* ----------------
322  *              index_rescan  - (re)start a scan of an index
323  *
324  * The caller may specify a new set of scankeys (but the number of keys
325  * cannot change).      To restart the scan without changing keys, pass NULL
326  * for the key array.
327  *
328  * Note that this is also called when first starting an indexscan;
329  * see RelationGetIndexScan.  Keys *must* be passed in that case,
330  * unless scan->numberOfKeys is zero.
331  * ----------------
332  */
333 void
334 index_rescan(IndexScanDesc scan, ScanKey key)
335 {
336         FmgrInfo   *procedure;
337
338         SCAN_CHECKS;
339         GET_SCAN_PROCEDURE(amrescan);
340
341         /* Release any held pin on a heap page */
342         if (BufferIsValid(scan->xs_cbuf))
343         {
344                 ReleaseBuffer(scan->xs_cbuf);
345                 scan->xs_cbuf = InvalidBuffer;
346         }
347
348         scan->kill_prior_tuple = false;         /* for safety */
349         scan->keys_are_unique = false;          /* may be set by index AM */
350         scan->got_tuple = false;
351         scan->unique_tuple_pos = 0;
352         scan->unique_tuple_mark = 0;
353
354         FunctionCall2(procedure,
355                                   PointerGetDatum(scan),
356                                   PointerGetDatum(key));
357 }
358
359 /* ----------------
360  *              index_endscan - end a scan
361  * ----------------
362  */
363 void
364 index_endscan(IndexScanDesc scan)
365 {
366         FmgrInfo   *procedure;
367
368         SCAN_CHECKS;
369         GET_SCAN_PROCEDURE(amendscan);
370
371         /* Release any held pin on a heap page */
372         if (BufferIsValid(scan->xs_cbuf))
373         {
374                 ReleaseBuffer(scan->xs_cbuf);
375                 scan->xs_cbuf = InvalidBuffer;
376         }
377
378         /* End the AM's scan */
379         FunctionCall1(procedure, PointerGetDatum(scan));
380
381         /* Release index lock and refcount acquired by index_beginscan */
382
383         UnlockRelation(scan->indexRelation, AccessShareLock);
384
385         RelationDecrementReferenceCount(scan->indexRelation);
386
387         /* Release the scan data structure itself */
388         IndexScanEnd(scan);
389 }
390
391 /* ----------------
392  *              index_markpos  - mark a scan position
393  * ----------------
394  */
395 void
396 index_markpos(IndexScanDesc scan)
397 {
398         FmgrInfo   *procedure;
399
400         SCAN_CHECKS;
401         GET_SCAN_PROCEDURE(ammarkpos);
402
403         scan->unique_tuple_mark = scan->unique_tuple_pos;
404
405         FunctionCall1(procedure, PointerGetDatum(scan));
406 }
407
408 /* ----------------
409  *              index_restrpos  - restore a scan position
410  *
411  * NOTE: this only restores the internal scan state of the index AM.
412  * The current result tuple (scan->xs_ctup) doesn't change.  See comments
413  * for ExecRestrPos().
414  * ----------------
415  */
416 void
417 index_restrpos(IndexScanDesc scan)
418 {
419         FmgrInfo   *procedure;
420
421         SCAN_CHECKS;
422         GET_SCAN_PROCEDURE(amrestrpos);
423
424         scan->kill_prior_tuple = false;         /* for safety */
425
426         /*
427          * We do not reset got_tuple; so if the scan is actually being
428          * short-circuited by index_getnext, the effective position
429          * restoration is done by restoring unique_tuple_pos.
430          */
431         scan->unique_tuple_pos = scan->unique_tuple_mark;
432
433         FunctionCall1(procedure, PointerGetDatum(scan));
434 }
435
436 /* ----------------
437  *              index_getnext - get the next heap tuple from a scan
438  *
439  * The result is the next heap tuple satisfying the scan keys and the
440  * snapshot, or NULL if no more matching tuples exist.  On success,
441  * the buffer containing the heap tuple is pinned (the pin will be dropped
442  * at the next index_getnext or index_endscan).  The index TID corresponding
443  * to the heap tuple can be obtained if needed from scan->currentItemData.
444  * ----------------
445  */
446 HeapTuple
447 index_getnext(IndexScanDesc scan, ScanDirection direction)
448 {
449         HeapTuple       heapTuple = &scan->xs_ctup;
450         FmgrInfo   *procedure;
451
452         SCAN_CHECKS;
453         GET_SCAN_PROCEDURE(amgettuple);
454
455         /*
456          * If we already got a tuple and it must be unique, there's no need to
457          * make the index AM look through any additional tuples.  (This can
458          * save a useful amount of work in scenarios where there are many dead
459          * tuples due to heavy update activity.)
460          *
461          * To do this we must keep track of the logical scan position
462          * (before/on/after tuple).  Also, we have to be sure to release scan
463          * resources before returning NULL; if we fail to do so then a
464          * multi-index scan can easily run the system out of free buffers.      We
465          * can release index-level resources fairly cheaply by calling
466          * index_rescan.  This means there are two persistent states as far as
467          * the index AM is concerned: on-tuple and rescanned.  If we are
468          * actually asked to re-fetch the single tuple, we have to go through
469          * a fresh indexscan startup, which penalizes that (infrequent) case.
470          */
471         if (scan->keys_are_unique && scan->got_tuple)
472         {
473                 int                     new_tuple_pos = scan->unique_tuple_pos;
474
475                 if (ScanDirectionIsForward(direction))
476                 {
477                         if (new_tuple_pos <= 0)
478                                 new_tuple_pos++;
479                 }
480                 else
481                 {
482                         if (new_tuple_pos >= 0)
483                                 new_tuple_pos--;
484                 }
485                 if (new_tuple_pos == 0)
486                 {
487                         /*
488                          * We are moving onto the unique tuple from having been off
489                          * it. We just fall through and let the index AM do the work.
490                          * Note we should get the right answer regardless of scan
491                          * direction.
492                          */
493                         scan->unique_tuple_pos = 0; /* need to update position */
494                 }
495                 else
496                 {
497                         /*
498                          * Moving off the tuple; must do amrescan to release
499                          * index-level pins before we return NULL.      Since index_rescan
500                          * will reset my state, must save and restore...
501                          */
502                         int                     unique_tuple_mark = scan->unique_tuple_mark;
503
504                         index_rescan(scan, NULL /* no change to key */ );
505
506                         scan->keys_are_unique = true;
507                         scan->got_tuple = true;
508                         scan->unique_tuple_pos = new_tuple_pos;
509                         scan->unique_tuple_mark = unique_tuple_mark;
510
511                         return NULL;
512                 }
513         }
514
515         /* just make sure this is false... */
516         scan->kill_prior_tuple = false;
517
518         for (;;)
519         {
520                 bool            found;
521
522                 /*
523                  * The AM's gettuple proc finds the next tuple matching the scan
524                  * keys.
525                  */
526                 found = DatumGetBool(FunctionCall2(procedure,
527                                                                                    PointerGetDatum(scan),
528                                                                                    Int32GetDatum(direction)));
529
530                 /* Reset kill flag immediately for safety */
531                 scan->kill_prior_tuple = false;
532
533                 if (!found)
534                 {
535                         /* Release any held pin on a heap page */
536                         if (BufferIsValid(scan->xs_cbuf))
537                         {
538                                 ReleaseBuffer(scan->xs_cbuf);
539                                 scan->xs_cbuf = InvalidBuffer;
540                         }
541                         return NULL;            /* failure exit */
542                 }
543
544                 pgstat_count_index_tuples(&scan->xs_pgstat_info, 1);
545
546                 /*
547                  * Fetch the heap tuple and see if it matches the snapshot.
548                  */
549                 if (heap_release_fetch(scan->heapRelation, scan->xs_snapshot,
550                                                            heapTuple, &scan->xs_cbuf, true,
551                                                            &scan->xs_pgstat_info))
552                         break;
553
554                 /* Skip if no undeleted tuple at this location */
555                 if (heapTuple->t_data == NULL)
556                         continue;
557
558                 /*
559                  * If we can't see it, maybe no one else can either.  Check to see
560                  * if the tuple is dead to all transactions.  If so, signal the
561                  * index AM to not return it on future indexscans.
562                  *
563                  * We told heap_release_fetch to keep a pin on the buffer, so we can
564                  * re-access the tuple here.  But we must re-lock the buffer first.
565                  */
566                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
567
568                 if (HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
569                                                                          scan->xs_cbuf) == HEAPTUPLE_DEAD)
570                         scan->kill_prior_tuple = true;
571
572                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
573         }
574
575         /* Success exit */
576         scan->got_tuple = true;
577
578         /*
579          * If we just fetched a known-unique tuple, then subsequent calls will
580          * go through the short-circuit code above.  unique_tuple_pos has been
581          * initialized to 0, which is the correct state ("on row").
582          */
583
584         return heapTuple;
585 }
586
587 /* ----------------
588  *              index_getnext_indexitem - get the next index tuple from a scan
589  *
590  * Finds the next index tuple satisfying the scan keys.  Note that the
591  * corresponding heap tuple is not accessed, and thus no time qual (snapshot)
592  * check is done, other than the index AM's internal check for killed tuples
593  * (which most callers of this routine will probably want to suppress by
594  * setting scan->ignore_killed_tuples = false).
595  *
596  * On success (TRUE return), the found index TID is in scan->currentItemData,
597  * and its heap TID is in scan->xs_ctup.t_self.  scan->xs_cbuf is untouched.
598  * ----------------
599  */
600 bool
601 index_getnext_indexitem(IndexScanDesc scan,
602                                                 ScanDirection direction)
603 {
604         FmgrInfo   *procedure;
605         bool            found;
606
607         SCAN_CHECKS;
608         GET_SCAN_PROCEDURE(amgettuple);
609
610         /* just make sure this is false... */
611         scan->kill_prior_tuple = false;
612
613         /*
614          * have the am's gettuple proc do all the work.
615          */
616         found = DatumGetBool(FunctionCall2(procedure,
617                                                                            PointerGetDatum(scan),
618                                                                            Int32GetDatum(direction)));
619
620         if (found)
621                 pgstat_count_index_tuples(&scan->xs_pgstat_info, 1);
622
623         return found;
624 }
625
626 /* ----------------
627  *              index_getmulti - get multiple tuples from an index scan
628  *
629  * Collects the TIDs of multiple heap tuples satisfying the scan keys.
630  * Since there's no interlock between the index scan and the eventual heap
631  * access, this is only safe to use with MVCC-based snapshots: the heap
632  * item slot could have been replaced by a newer tuple by the time we get
633  * to it.
634  *
635  * A TRUE result indicates more calls should occur; a FALSE result says the
636  * scan is done.  *returned_tids could be zero or nonzero in either case.
637  * ----------------
638  */
639 bool
640 index_getmulti(IndexScanDesc scan,
641                            ItemPointer tids, int32 max_tids,
642                            int32 *returned_tids)
643 {
644         FmgrInfo   *procedure;
645         bool            found;
646
647         SCAN_CHECKS;
648         GET_SCAN_PROCEDURE(amgetmulti);
649
650         /* just make sure this is false... */
651         scan->kill_prior_tuple = false;
652
653         /*
654          * have the am's getmulti proc do all the work.
655          */
656         found = DatumGetBool(FunctionCall4(procedure,
657                                                                            PointerGetDatum(scan),
658                                                                            PointerGetDatum(tids),
659                                                                            Int32GetDatum(max_tids),
660                                                                            PointerGetDatum(returned_tids)));
661
662         pgstat_count_index_tuples(&scan->xs_pgstat_info, *returned_tids);
663
664         return found;
665 }
666
667 /* ----------------
668  *              index_bulk_delete - do mass deletion of index entries
669  *
670  *              callback routine tells whether a given main-heap tuple is
671  *              to be deleted
672  *
673  *              return value is an optional palloc'd struct of statistics
674  * ----------------
675  */
676 IndexBulkDeleteResult *
677 index_bulk_delete(Relation indexRelation,
678                                   IndexBulkDeleteCallback callback,
679                                   void *callback_state)
680 {
681         FmgrInfo   *procedure;
682         IndexBulkDeleteResult *result;
683
684         RELATION_CHECKS;
685         GET_REL_PROCEDURE(ambulkdelete);
686
687         result = (IndexBulkDeleteResult *)
688                 DatumGetPointer(FunctionCall3(procedure,
689                                                                           PointerGetDatum(indexRelation),
690                                                                           PointerGetDatum((Pointer) callback),
691                                                                           PointerGetDatum(callback_state)));
692
693         return result;
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 *
703 index_vacuum_cleanup(Relation indexRelation,
704                                          IndexVacuumCleanupInfo *info,
705                                          IndexBulkDeleteResult *stats)
706 {
707         FmgrInfo   *procedure;
708         IndexBulkDeleteResult *result;
709
710         RELATION_CHECKS;
711
712         /* It's okay for an index AM not to have a vacuumcleanup procedure */
713         if (!RegProcedureIsValid(indexRelation->rd_am->amvacuumcleanup))
714                 return stats;
715
716         GET_REL_PROCEDURE(amvacuumcleanup);
717
718         result = (IndexBulkDeleteResult *)
719                 DatumGetPointer(FunctionCall3(procedure,
720                                                                           PointerGetDatum(indexRelation),
721                                                                           PointerGetDatum((Pointer) info),
722                                                                           PointerGetDatum((Pointer) stats)));
723
724         return result;
725 }
726
727 /* ----------------
728  *              index_getprocid
729  *
730  *              Some indexed access methods may require support routines that are
731  *              not in the operator class/operator model imposed by pg_am.      These
732  *              access methods may store the OIDs of registered procedures they
733  *              need in pg_amproc.      These registered procedure OIDs are ordered in
734  *              a way that makes sense to the access method, and used only by the
735  *              access method.  The general index code doesn't know anything about
736  *              the routines involved; it just builds an ordered list of them for
737  *              each attribute on which an index is defined.
738  *
739  *              This routine returns the requested procedure OID for a particular
740  *              indexed attribute.
741  * ----------------
742  */
743 RegProcedure
744 index_getprocid(Relation irel,
745                                 AttrNumber attnum,
746                                 uint16 procnum)
747 {
748         RegProcedure *loc;
749         int                     nproc;
750         int                     procindex;
751
752         nproc = irel->rd_am->amsupport;
753
754         Assert(procnum > 0 && procnum <= (uint16) nproc);
755
756         procindex = (nproc * (attnum - 1)) + (procnum - 1);
757
758         loc = irel->rd_support;
759
760         Assert(loc != NULL);
761
762         return loc[procindex];
763 }
764
765 /* ----------------
766  *              index_getprocinfo
767  *
768  *              This routine allows index AMs to keep fmgr lookup info for
769  *              support procs in the relcache.
770  *
771  * Note: the return value points into cached data that will be lost during
772  * any relcache rebuild!  Therefore, either use the callinfo right away,
773  * or save it only after having acquired some type of lock on the index rel.
774  * ----------------
775  */
776 FmgrInfo *
777 index_getprocinfo(Relation irel,
778                                   AttrNumber attnum,
779                                   uint16 procnum)
780 {
781         FmgrInfo   *locinfo;
782         int                     nproc;
783         int                     procindex;
784
785         nproc = irel->rd_am->amsupport;
786
787         Assert(procnum > 0 && procnum <= (uint16) nproc);
788
789         procindex = (nproc * (attnum - 1)) + (procnum - 1);
790
791         locinfo = irel->rd_supportinfo;
792
793         Assert(locinfo != NULL);
794
795         locinfo += procindex;
796
797         /* Initialize the lookup info if first time through */
798         if (locinfo->fn_oid == InvalidOid)
799         {
800                 RegProcedure *loc = irel->rd_support;
801                 RegProcedure procId;
802
803                 Assert(loc != NULL);
804
805                 procId = loc[procindex];
806
807                 /*
808                  * Complain if function was not found during
809                  * IndexSupportInitialize. This should not happen unless the
810                  * system tables contain bogus entries for the index opclass.  (If
811                  * an AM wants to allow a support function to be optional, it can
812                  * use index_getprocid.)
813                  */
814                 if (!RegProcedureIsValid(procId))
815                         elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
816                                  procnum, attnum, RelationGetRelationName(irel));
817
818                 fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
819         }
820
821         return locinfo;
822 }