]> granicus.if.org Git - postgresql/blob - src/backend/access/index/indexam.c
624b53d635cd0a7e9c0a2a67051bd041bba05601
[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.84 2005/06/27 12:45:22 teodor 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 "utils/relcache.h"
69
70 #include "pgstat.h"
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         pgstat_reset_index_scan(&scan->xs_pgstat_info);
359 }
360
361 /* ----------------
362  *              index_endscan - end a scan
363  * ----------------
364  */
365 void
366 index_endscan(IndexScanDesc scan)
367 {
368         FmgrInfo   *procedure;
369
370         SCAN_CHECKS;
371         GET_SCAN_PROCEDURE(amendscan);
372
373         /* Release any held pin on a heap page */
374         if (BufferIsValid(scan->xs_cbuf))
375         {
376                 ReleaseBuffer(scan->xs_cbuf);
377                 scan->xs_cbuf = InvalidBuffer;
378         }
379
380         /* End the AM's scan */
381         FunctionCall1(procedure, PointerGetDatum(scan));
382
383         /* Release index lock and refcount acquired by index_beginscan */
384
385         UnlockRelation(scan->indexRelation, AccessShareLock);
386
387         RelationDecrementReferenceCount(scan->indexRelation);
388
389         /* Release the scan data structure itself */
390         IndexScanEnd(scan);
391 }
392
393 /* ----------------
394  *              index_markpos  - mark a scan position
395  * ----------------
396  */
397 void
398 index_markpos(IndexScanDesc scan)
399 {
400         FmgrInfo   *procedure;
401
402         SCAN_CHECKS;
403         GET_SCAN_PROCEDURE(ammarkpos);
404
405         scan->unique_tuple_mark = scan->unique_tuple_pos;
406
407         FunctionCall1(procedure, PointerGetDatum(scan));
408 }
409
410 /* ----------------
411  *              index_restrpos  - restore a scan position
412  *
413  * NOTE: this only restores the internal scan state of the index AM.
414  * The current result tuple (scan->xs_ctup) doesn't change.  See comments
415  * for ExecRestrPos().
416  * ----------------
417  */
418 void
419 index_restrpos(IndexScanDesc scan)
420 {
421         FmgrInfo   *procedure;
422
423         SCAN_CHECKS;
424         GET_SCAN_PROCEDURE(amrestrpos);
425
426         scan->kill_prior_tuple = false;         /* for safety */
427
428         /*
429          * We do not reset got_tuple; so if the scan is actually being
430          * short-circuited by index_getnext, the effective position
431          * restoration is done by restoring unique_tuple_pos.
432          */
433         scan->unique_tuple_pos = scan->unique_tuple_mark;
434
435         FunctionCall1(procedure, PointerGetDatum(scan));
436 }
437
438 /* ----------------
439  *              index_getnext - get the next heap tuple from a scan
440  *
441  * The result is the next heap tuple satisfying the scan keys and the
442  * snapshot, or NULL if no more matching tuples exist.  On success,
443  * the buffer containing the heap tuple is pinned (the pin will be dropped
444  * at the next index_getnext or index_endscan).  The index TID corresponding
445  * to the heap tuple can be obtained if needed from scan->currentItemData.
446  * ----------------
447  */
448 HeapTuple
449 index_getnext(IndexScanDesc scan, ScanDirection direction)
450 {
451         HeapTuple       heapTuple = &scan->xs_ctup;
452         FmgrInfo   *procedure;
453
454         SCAN_CHECKS;
455         GET_SCAN_PROCEDURE(amgettuple);
456
457         /*
458          * If we already got a tuple and it must be unique, there's no need to
459          * make the index AM look through any additional tuples.  (This can
460          * save a useful amount of work in scenarios where there are many dead
461          * tuples due to heavy update activity.)
462          *
463          * To do this we must keep track of the logical scan position
464          * (before/on/after tuple).  Also, we have to be sure to release scan
465          * resources before returning NULL; if we fail to do so then a
466          * multi-index scan can easily run the system out of free buffers.      We
467          * can release index-level resources fairly cheaply by calling
468          * index_rescan.  This means there are two persistent states as far as
469          * the index AM is concerned: on-tuple and rescanned.  If we are
470          * actually asked to re-fetch the single tuple, we have to go through
471          * a fresh indexscan startup, which penalizes that (infrequent) case.
472          */
473         if (scan->keys_are_unique && scan->got_tuple)
474         {
475                 int                     new_tuple_pos = scan->unique_tuple_pos;
476
477                 if (ScanDirectionIsForward(direction))
478                 {
479                         if (new_tuple_pos <= 0)
480                                 new_tuple_pos++;
481                 }
482                 else
483                 {
484                         if (new_tuple_pos >= 0)
485                                 new_tuple_pos--;
486                 }
487                 if (new_tuple_pos == 0)
488                 {
489                         /*
490                          * We are moving onto the unique tuple from having been off
491                          * it. We just fall through and let the index AM do the work.
492                          * Note we should get the right answer regardless of scan
493                          * direction.
494                          */
495                         scan->unique_tuple_pos = 0; /* need to update position */
496                 }
497                 else
498                 {
499                         /*
500                          * Moving off the tuple; must do amrescan to release
501                          * index-level pins before we return NULL.      Since index_rescan
502                          * will reset my state, must save and restore...
503                          */
504                         int                     unique_tuple_mark = scan->unique_tuple_mark;
505
506                         index_rescan(scan, NULL /* no change to key */ );
507
508                         scan->keys_are_unique = true;
509                         scan->got_tuple = true;
510                         scan->unique_tuple_pos = new_tuple_pos;
511                         scan->unique_tuple_mark = unique_tuple_mark;
512
513                         return NULL;
514                 }
515         }
516
517         /* just make sure this is false... */
518         scan->kill_prior_tuple = false;
519
520         for (;;)
521         {
522                 bool            found;
523
524                 pgstat_count_index_scan(&scan->xs_pgstat_info);
525
526                 /*
527                  * The AM's gettuple proc finds the next tuple matching the scan
528                  * keys.
529                  */
530                 found = DatumGetBool(FunctionCall2(procedure,
531                                                                                    PointerGetDatum(scan),
532                                                                                    Int32GetDatum(direction)));
533
534                 /* Reset kill flag immediately for safety */
535                 scan->kill_prior_tuple = false;
536
537                 if (!found)
538                 {
539                         /* Release any held pin on a heap page */
540                         if (BufferIsValid(scan->xs_cbuf))
541                         {
542                                 ReleaseBuffer(scan->xs_cbuf);
543                                 scan->xs_cbuf = InvalidBuffer;
544                         }
545                         return NULL;            /* failure exit */
546                 }
547
548                 /*
549                  * Fetch the heap tuple and see if it matches the snapshot.
550                  */
551                 if (heap_release_fetch(scan->heapRelation, scan->xs_snapshot,
552                                                            heapTuple, &scan->xs_cbuf, true,
553                                                            &scan->xs_pgstat_info))
554                         break;
555
556                 /* Skip if no undeleted tuple at this location */
557                 if (heapTuple->t_data == NULL)
558                         continue;
559
560                 /*
561                  * If we can't see it, maybe no one else can either.  Check to see
562                  * if the tuple is dead to all transactions.  If so, signal the
563                  * index AM to not return it on future indexscans.
564                  *
565                  * We told heap_release_fetch to keep a pin on the buffer, so we can
566                  * re-access the tuple here.  But we must re-lock the buffer first.
567                  */
568                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
569
570                 if (HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
571                                                                          scan->xs_cbuf) == HEAPTUPLE_DEAD)
572                         scan->kill_prior_tuple = true;
573
574                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
575         }
576
577         /* Success exit */
578         scan->got_tuple = true;
579
580         /*
581          * If we just fetched a known-unique tuple, then subsequent calls will
582          * go through the short-circuit code above.  unique_tuple_pos has been
583          * initialized to 0, which is the correct state ("on row").
584          */
585
586         pgstat_count_index_getnext(&scan->xs_pgstat_info);
587
588         return heapTuple;
589 }
590
591 /* ----------------
592  *              index_getnext_indexitem - get the next index tuple from a scan
593  *
594  * Finds the next index tuple satisfying the scan keys.  Note that the
595  * corresponding heap tuple is not accessed, and thus no time qual (snapshot)
596  * check is done, other than the index AM's internal check for killed tuples
597  * (which most callers of this routine will probably want to suppress by
598  * setting scan->ignore_killed_tuples = false).
599  *
600  * On success (TRUE return), the found index TID is in scan->currentItemData,
601  * and its heap TID is in scan->xs_ctup.t_self.  scan->xs_cbuf is untouched.
602  * ----------------
603  */
604 bool
605 index_getnext_indexitem(IndexScanDesc scan,
606                                                 ScanDirection direction)
607 {
608         FmgrInfo   *procedure;
609         bool            found;
610
611         SCAN_CHECKS;
612         GET_SCAN_PROCEDURE(amgettuple);
613
614         /* just make sure this is false... */
615         scan->kill_prior_tuple = false;
616
617         /*
618          * have the am's gettuple proc do all the work.
619          */
620         found = DatumGetBool(FunctionCall2(procedure,
621                                                                            PointerGetDatum(scan),
622                                                                            Int32GetDatum(direction)));
623
624         return found;
625 }
626
627 /* ----------------
628  *              index_getmulti - get multiple tuples from an index scan
629  *
630  * Collects the TIDs of multiple heap tuples satisfying the scan keys.
631  * Since there's no interlock between the index scan and the eventual heap
632  * access, this is only safe to use with MVCC-based snapshots: the heap
633  * item slot could have been replaced by a newer tuple by the time we get
634  * to it.
635  *
636  * A TRUE result indicates more calls should occur; a FALSE result says the
637  * scan is done.  *returned_tids could be zero or nonzero in either case.
638  * ----------------
639  */
640 bool
641 index_getmulti(IndexScanDesc scan,
642                            ItemPointer tids, int32 max_tids,
643                            int32 *returned_tids)
644 {
645         FmgrInfo   *procedure;
646         bool            found;
647
648         SCAN_CHECKS;
649         GET_SCAN_PROCEDURE(amgetmulti);
650
651         /* just make sure this is false... */
652         scan->kill_prior_tuple = false;
653
654         /*
655          * have the am's getmulti proc do all the work.
656          */
657         found = DatumGetBool(FunctionCall4(procedure,
658                                                                            PointerGetDatum(scan),
659                                                                            PointerGetDatum(tids),
660                                                                            Int32GetDatum(max_tids),
661                                                                            PointerGetDatum(returned_tids)));
662
663         return found;
664 }
665
666 /* ----------------
667  *              index_bulk_delete - do mass deletion of index entries
668  *
669  *              callback routine tells whether a given main-heap tuple is
670  *              to be deleted
671  *
672  *              return value is an optional palloc'd struct of statistics
673  * ----------------
674  */
675 IndexBulkDeleteResult *
676 index_bulk_delete(Relation indexRelation,
677                                   IndexBulkDeleteCallback callback,
678                                   void *callback_state)
679 {
680         FmgrInfo   *procedure;
681         IndexBulkDeleteResult *result;
682
683         RELATION_CHECKS;
684         GET_REL_PROCEDURE(ambulkdelete);
685
686         result = (IndexBulkDeleteResult *)
687                 DatumGetPointer(FunctionCall3(procedure,
688                                                                           PointerGetDatum(indexRelation),
689                                                                           PointerGetDatum((Pointer) callback),
690                                                                           PointerGetDatum(callback_state)));
691
692         return result;
693 }
694
695 /* ----------------
696  *              index_vacuum_cleanup - do post-deletion cleanup of an index
697  *
698  *              return value is an optional palloc'd struct of statistics
699  * ----------------
700  */
701 IndexBulkDeleteResult *
702 index_vacuum_cleanup(Relation indexRelation,
703                                          IndexVacuumCleanupInfo *info,
704                                          IndexBulkDeleteResult *stats)
705 {
706         FmgrInfo   *procedure;
707         IndexBulkDeleteResult *result;
708
709         RELATION_CHECKS;
710
711         /* It's okay for an index AM not to have a vacuumcleanup procedure */
712         if (!RegProcedureIsValid(indexRelation->rd_am->amvacuumcleanup))
713                 return stats;
714
715         GET_REL_PROCEDURE(amvacuumcleanup);
716
717         result = (IndexBulkDeleteResult *)
718                 DatumGetPointer(FunctionCall3(procedure,
719                                                                           PointerGetDatum(indexRelation),
720                                                                           PointerGetDatum((Pointer) info),
721                                                                           PointerGetDatum((Pointer) stats)));
722
723         return result;
724 }
725
726 /* ----------------
727  *              index_getprocid
728  *
729  *              Some indexed access methods may require support routines that are
730  *              not in the operator class/operator model imposed by pg_am.      These
731  *              access methods may store the OIDs of registered procedures they
732  *              need in pg_amproc.      These registered procedure OIDs are ordered in
733  *              a way that makes sense to the access method, and used only by the
734  *              access method.  The general index code doesn't know anything about
735  *              the routines involved; it just builds an ordered list of them for
736  *              each attribute on which an index is defined.
737  *
738  *              This routine returns the requested procedure OID for a particular
739  *              indexed attribute.
740  * ----------------
741  */
742 RegProcedure
743 index_getprocid(Relation irel,
744                                 AttrNumber attnum,
745                                 uint16 procnum)
746 {
747         RegProcedure *loc;
748         int                     nproc;
749         int                     procindex;
750
751         nproc = irel->rd_am->amsupport;
752
753         Assert(procnum > 0 && procnum <= (uint16) nproc);
754
755         procindex = (nproc * (attnum - 1)) + (procnum - 1);
756
757         loc = irel->rd_support;
758
759         Assert(loc != NULL);
760
761         return loc[procindex];
762 }
763
764 /* ----------------
765  *              index_getprocinfo
766  *
767  *              This routine allows index AMs to keep fmgr lookup info for
768  *              support procs in the relcache.
769  *
770  * Note: the return value points into cached data that will be lost during
771  * any relcache rebuild!  Therefore, either use the callinfo right away,
772  * or save it only after having acquired some type of lock on the index rel.
773  * ----------------
774  */
775 FmgrInfo *
776 index_getprocinfo(Relation irel,
777                                   AttrNumber attnum,
778                                   uint16 procnum)
779 {
780         FmgrInfo   *locinfo;
781         int                     nproc;
782         int                     procindex;
783
784         nproc = irel->rd_am->amsupport;
785
786         Assert(procnum > 0 && procnum <= (uint16) nproc);
787
788         procindex = (nproc * (attnum - 1)) + (procnum - 1);
789
790         locinfo = irel->rd_supportinfo;
791
792         Assert(locinfo != NULL);
793
794         locinfo += procindex;
795
796         /* Initialize the lookup info if first time through */
797         if (locinfo->fn_oid == InvalidOid)
798         {
799                 RegProcedure *loc = irel->rd_support;
800                 RegProcedure procId;
801
802                 Assert(loc != NULL);
803
804                 procId = loc[procindex];
805
806                 /*
807                  * Complain if function was not found during
808                  * IndexSupportInitialize. This should not happen unless the
809                  * system tables contain bogus entries for the index opclass.  (If
810                  * an AM wants to allow a support function to be optional, it can
811                  * use index_getprocid.)
812                  */
813                 if (!RegProcedureIsValid(procId))
814                         elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
815                                  procnum, attnum, RelationGetRelationName(irel));
816
817                 fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
818         }
819
820         return locinfo;
821 }