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