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