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