]> granicus.if.org Git - postgresql/blob - src/backend/access/index/indexam.c
Tweak indexscan machinery to avoid taking an AccessShareLock on an index
[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.87 2005/12/03 05:51:00 tgl Exp $
12  *
13  * INTERFACE ROUTINES
14  *              index_open              - open an index relation by relation OID
15  *              index_openrv    - open an index relation specified by a RangeVar
16  *              index_close             - close an index relation
17  *              index_beginscan - start a scan of an index with amgettuple
18  *              index_beginscan_multi - start a scan of an index with amgetmulti
19  *              index_rescan    - restart a scan of an index
20  *              index_endscan   - end a scan
21  *              index_insert    - insert an index tuple into a relation
22  *              index_markpos   - mark a scan position
23  *              index_restrpos  - restore a scan position
24  *              index_getnext   - get the next tuple from a scan
25  *              index_getmulti  - get multiple tuples from a scan
26  *              index_bulk_delete       - bulk deletion of index tuples
27  *              index_vacuum_cleanup    - post-deletion cleanup of an index
28  *              index_getprocid - get a support procedure OID
29  *              index_getprocinfo - get a support procedure's lookup info
30  *
31  * NOTES
32  *              This file contains the index_ routines which used
33  *              to be a scattered collection of stuff in access/genam.
34  *
35  *
36  * old comments
37  *              Scans are implemented as follows:
38  *
39  *              `0' represents an invalid item pointer.
40  *              `-' represents an unknown item pointer.
41  *              `X' represents a known item pointers.
42  *              `+' represents known or invalid item pointers.
43  *              `*' represents any item pointers.
44  *
45  *              State is represented by a triple of these symbols in the order of
46  *              previous, current, next.  Note that the case of reverse scans works
47  *              identically.
48  *
49  *                              State   Result
50  *              (1)             + + -   + 0 0                   (if the next item pointer is invalid)
51  *              (2)                             + X -                   (otherwise)
52  *              (3)             * 0 0   * 0 0                   (no change)
53  *              (4)             + X 0   X 0 0                   (shift)
54  *              (5)             * + X   + X -                   (shift, add unknown)
55  *
56  *              All other states cannot occur.
57  *
58  *              Note: It would be possible to cache the status of the previous and
59  *                        next item pointer using the flags.
60  *
61  *-------------------------------------------------------------------------
62  */
63
64 #include "postgres.h"
65
66 #include "access/genam.h"
67 #include "access/heapam.h"
68 #include "pgstat.h"
69 #include "utils/relcache.h"
70
71
72 /* ----------------------------------------------------------------
73  *                                      macros used in index_ routines
74  * ----------------------------------------------------------------
75  */
76 #define RELATION_CHECKS \
77 ( \
78         AssertMacro(RelationIsValid(indexRelation)), \
79         AssertMacro(PointerIsValid(indexRelation->rd_am)) \
80 )
81
82 #define SCAN_CHECKS \
83 ( \
84         AssertMacro(IndexScanIsValid(scan)), \
85         AssertMacro(RelationIsValid(scan->indexRelation)), \
86         AssertMacro(PointerIsValid(scan->indexRelation->rd_am)) \
87 )
88
89 #define GET_REL_PROCEDURE(pname) \
90 do { \
91         procedure = &indexRelation->rd_aminfo->pname; \
92         if (!OidIsValid(procedure->fn_oid)) \
93         { \
94                 RegProcedure    procOid = indexRelation->rd_am->pname; \
95                 if (!RegProcedureIsValid(procOid)) \
96                         elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
97                 fmgr_info_cxt(procOid, procedure, indexRelation->rd_indexcxt); \
98         } \
99 } while(0)
100
101 #define GET_SCAN_PROCEDURE(pname) \
102 do { \
103         procedure = &scan->indexRelation->rd_aminfo->pname; \
104         if (!OidIsValid(procedure->fn_oid)) \
105         { \
106                 RegProcedure    procOid = scan->indexRelation->rd_am->pname; \
107                 if (!RegProcedureIsValid(procOid)) \
108                         elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
109                 fmgr_info_cxt(procOid, procedure, scan->indexRelation->rd_indexcxt); \
110         } \
111 } while(0)
112
113 static IndexScanDesc index_beginscan_internal(Relation indexRelation,
114                                                  bool need_index_lock,
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  * Most callers should pass need_index_lock = true to cause the index code
235  * to take AccessShareLock on the index for the duration of the scan.  But
236  * if it is known that a lock is already held on the index, pass false to
237  * skip taking an unnecessary lock.
238  */
239 IndexScanDesc
240 index_beginscan(Relation heapRelation,
241                                 Relation indexRelation,
242                                 bool need_index_lock,
243                                 Snapshot snapshot,
244                                 int nkeys, ScanKey key)
245 {
246         IndexScanDesc scan;
247
248         scan = index_beginscan_internal(indexRelation, need_index_lock,
249                                                                         nkeys, key);
250
251         /*
252          * Save additional parameters into the scandesc.  Everything else was set
253          * up by RelationGetIndexScan.
254          */
255         scan->is_multiscan = false;
256         scan->heapRelation = heapRelation;
257         scan->xs_snapshot = snapshot;
258
259         return scan;
260 }
261
262 /*
263  * index_beginscan_multi - start a scan of an index with amgetmulti
264  *
265  * As above, caller had better be holding some lock on the parent heap
266  * relation, even though it's not explicitly mentioned here.
267  */
268 IndexScanDesc
269 index_beginscan_multi(Relation indexRelation,
270                                           bool need_index_lock,
271                                           Snapshot snapshot,
272                                           int nkeys, ScanKey key)
273 {
274         IndexScanDesc scan;
275
276         scan = index_beginscan_internal(indexRelation, need_index_lock,
277                                                                         nkeys, key);
278
279         /*
280          * Save additional parameters into the scandesc.  Everything else was set
281          * up by RelationGetIndexScan.
282          */
283         scan->is_multiscan = true;
284         scan->xs_snapshot = snapshot;
285
286         return scan;
287 }
288
289 /*
290  * index_beginscan_internal --- common code for index_beginscan variants
291  */
292 static IndexScanDesc
293 index_beginscan_internal(Relation indexRelation,
294                                                  bool need_index_lock,
295                                                  int nkeys, ScanKey key)
296 {
297         IndexScanDesc scan;
298         FmgrInfo   *procedure;
299
300         RELATION_CHECKS;
301
302         RelationIncrementReferenceCount(indexRelation);
303
304         /*
305          * Acquire AccessShareLock for the duration of the scan, unless caller
306          * says it already has lock on the index.
307          *
308          * Note: we could get an SI inval message here and consequently have to
309          * rebuild the relcache entry.  The refcount increment above ensures that
310          * we will rebuild it and not just flush it...
311          */
312         if (need_index_lock)
313                 LockRelation(indexRelation, AccessShareLock);
314
315         /*
316          * LockRelation can clean rd_aminfo structure, so fill procedure after
317          * LockRelation
318          */
319
320         GET_REL_PROCEDURE(ambeginscan);
321
322         /*
323          * Tell the AM to open a scan.
324          */
325         scan = (IndexScanDesc)
326                 DatumGetPointer(FunctionCall3(procedure,
327                                                                           PointerGetDatum(indexRelation),
328                                                                           Int32GetDatum(nkeys),
329                                                                           PointerGetDatum(key)));
330
331         /* Save flag to tell index_endscan whether to release lock */
332         scan->have_lock = need_index_lock;
333
334         return scan;
335 }
336
337 /* ----------------
338  *              index_rescan  - (re)start a scan of an index
339  *
340  * The caller may specify a new set of scankeys (but the number of keys
341  * cannot change).      To restart the scan without changing keys, pass NULL
342  * for the key array.
343  *
344  * Note that this is also called when first starting an indexscan;
345  * see RelationGetIndexScan.  Keys *must* be passed in that case,
346  * unless scan->numberOfKeys is zero.
347  * ----------------
348  */
349 void
350 index_rescan(IndexScanDesc scan, ScanKey key)
351 {
352         FmgrInfo   *procedure;
353
354         SCAN_CHECKS;
355         GET_SCAN_PROCEDURE(amrescan);
356
357         /* Release any held pin on a heap page */
358         if (BufferIsValid(scan->xs_cbuf))
359         {
360                 ReleaseBuffer(scan->xs_cbuf);
361                 scan->xs_cbuf = InvalidBuffer;
362         }
363
364         scan->kill_prior_tuple = false;         /* for safety */
365         scan->keys_are_unique = false;          /* may be set by index AM */
366         scan->got_tuple = false;
367         scan->unique_tuple_pos = 0;
368         scan->unique_tuple_mark = 0;
369
370         FunctionCall2(procedure,
371                                   PointerGetDatum(scan),
372                                   PointerGetDatum(key));
373 }
374
375 /* ----------------
376  *              index_endscan - end a scan
377  * ----------------
378  */
379 void
380 index_endscan(IndexScanDesc scan)
381 {
382         FmgrInfo   *procedure;
383
384         SCAN_CHECKS;
385         GET_SCAN_PROCEDURE(amendscan);
386
387         /* Release any held pin on a heap page */
388         if (BufferIsValid(scan->xs_cbuf))
389         {
390                 ReleaseBuffer(scan->xs_cbuf);
391                 scan->xs_cbuf = InvalidBuffer;
392         }
393
394         /* End the AM's scan */
395         FunctionCall1(procedure, PointerGetDatum(scan));
396
397         /* Release index lock and refcount acquired by index_beginscan */
398
399         if (scan->have_lock)
400                 UnlockRelation(scan->indexRelation, AccessShareLock);
401
402         RelationDecrementReferenceCount(scan->indexRelation);
403
404         /* Release the scan data structure itself */
405         IndexScanEnd(scan);
406 }
407
408 /* ----------------
409  *              index_markpos  - mark a scan position
410  * ----------------
411  */
412 void
413 index_markpos(IndexScanDesc scan)
414 {
415         FmgrInfo   *procedure;
416
417         SCAN_CHECKS;
418         GET_SCAN_PROCEDURE(ammarkpos);
419
420         scan->unique_tuple_mark = scan->unique_tuple_pos;
421
422         FunctionCall1(procedure, PointerGetDatum(scan));
423 }
424
425 /* ----------------
426  *              index_restrpos  - restore a scan position
427  *
428  * NOTE: this only restores the internal scan state of the index AM.
429  * The current result tuple (scan->xs_ctup) doesn't change.  See comments
430  * for ExecRestrPos().
431  * ----------------
432  */
433 void
434 index_restrpos(IndexScanDesc scan)
435 {
436         FmgrInfo   *procedure;
437
438         SCAN_CHECKS;
439         GET_SCAN_PROCEDURE(amrestrpos);
440
441         scan->kill_prior_tuple = false;         /* for safety */
442
443         /*
444          * We do not reset got_tuple; so if the scan is actually being
445          * short-circuited by index_getnext, the effective position restoration is
446          * done by restoring unique_tuple_pos.
447          */
448         scan->unique_tuple_pos = scan->unique_tuple_mark;
449
450         FunctionCall1(procedure, PointerGetDatum(scan));
451 }
452
453 /* ----------------
454  *              index_getnext - get the next heap tuple from a scan
455  *
456  * The result is the next heap tuple satisfying the scan keys and the
457  * snapshot, or NULL if no more matching tuples exist.  On success,
458  * the buffer containing the heap tuple is pinned (the pin will be dropped
459  * at the next index_getnext or index_endscan).  The index TID corresponding
460  * to the heap tuple can be obtained if needed from scan->currentItemData.
461  * ----------------
462  */
463 HeapTuple
464 index_getnext(IndexScanDesc scan, ScanDirection direction)
465 {
466         HeapTuple       heapTuple = &scan->xs_ctup;
467         FmgrInfo   *procedure;
468
469         SCAN_CHECKS;
470         GET_SCAN_PROCEDURE(amgettuple);
471
472         /*
473          * If we already got a tuple and it must be unique, there's no need to
474          * make the index AM look through any additional tuples.  (This can save a
475          * useful amount of work in scenarios where there are many dead tuples due
476          * to heavy update activity.)
477          *
478          * To do this we must keep track of the logical scan position
479          * (before/on/after tuple).  Also, we have to be sure to release scan
480          * resources before returning NULL; if we fail to do so then a multi-index
481          * scan can easily run the system out of free buffers.  We can release
482          * index-level resources fairly cheaply by calling index_rescan.  This
483          * means there are two persistent states as far as the index AM is
484          * concerned: on-tuple and rescanned.  If we are actually asked to
485          * re-fetch the single tuple, we have to go through a fresh indexscan
486          * startup, which penalizes that (infrequent) case.
487          */
488         if (scan->keys_are_unique && scan->got_tuple)
489         {
490                 int                     new_tuple_pos = scan->unique_tuple_pos;
491
492                 if (ScanDirectionIsForward(direction))
493                 {
494                         if (new_tuple_pos <= 0)
495                                 new_tuple_pos++;
496                 }
497                 else
498                 {
499                         if (new_tuple_pos >= 0)
500                                 new_tuple_pos--;
501                 }
502                 if (new_tuple_pos == 0)
503                 {
504                         /*
505                          * We are moving onto the unique tuple from having been off it. We
506                          * just fall through and let the index AM do the work. Note we
507                          * should get the right answer regardless of scan direction.
508                          */
509                         scan->unique_tuple_pos = 0; /* need to update position */
510                 }
511                 else
512                 {
513                         /*
514                          * Moving off the tuple; must do amrescan to release index-level
515                          * pins before we return NULL.  Since index_rescan will reset my
516                          * state, must save and restore...
517                          */
518                         int                     unique_tuple_mark = scan->unique_tuple_mark;
519
520                         index_rescan(scan, NULL /* no change to key */ );
521
522                         scan->keys_are_unique = true;
523                         scan->got_tuple = true;
524                         scan->unique_tuple_pos = new_tuple_pos;
525                         scan->unique_tuple_mark = unique_tuple_mark;
526
527                         return NULL;
528                 }
529         }
530
531         /* just make sure this is false... */
532         scan->kill_prior_tuple = false;
533
534         for (;;)
535         {
536                 bool            found;
537
538                 /*
539                  * The AM's gettuple proc finds the next tuple matching the scan keys.
540                  */
541                 found = DatumGetBool(FunctionCall2(procedure,
542                                                                                    PointerGetDatum(scan),
543                                                                                    Int32GetDatum(direction)));
544
545                 /* Reset kill flag immediately for safety */
546                 scan->kill_prior_tuple = false;
547
548                 if (!found)
549                 {
550                         /* Release any held pin on a heap page */
551                         if (BufferIsValid(scan->xs_cbuf))
552                         {
553                                 ReleaseBuffer(scan->xs_cbuf);
554                                 scan->xs_cbuf = InvalidBuffer;
555                         }
556                         return NULL;            /* failure exit */
557                 }
558
559                 pgstat_count_index_tuples(&scan->xs_pgstat_info, 1);
560
561                 /*
562                  * Fetch the heap tuple and see if it matches the snapshot.
563                  */
564                 if (heap_release_fetch(scan->heapRelation, scan->xs_snapshot,
565                                                            heapTuple, &scan->xs_cbuf, true,
566                                                            &scan->xs_pgstat_info))
567                         break;
568
569                 /* Skip if no undeleted tuple at this location */
570                 if (heapTuple->t_data == NULL)
571                         continue;
572
573                 /*
574                  * If we can't see it, maybe no one else can either.  Check to see if
575                  * the tuple is dead to all transactions.  If so, signal the index AM
576                  * to not return it on future indexscans.
577                  *
578                  * We told heap_release_fetch to keep a pin on the buffer, so we can
579                  * re-access the tuple here.  But we must re-lock the buffer first.
580                  */
581                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
582
583                 if (HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
584                                                                          scan->xs_cbuf) == HEAPTUPLE_DEAD)
585                         scan->kill_prior_tuple = true;
586
587                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
588         }
589
590         /* Success exit */
591         scan->got_tuple = true;
592
593         /*
594          * If we just fetched a known-unique tuple, then subsequent calls will go
595          * through the short-circuit code above.  unique_tuple_pos has been
596          * initialized to 0, which is the correct state ("on row").
597          */
598
599         return heapTuple;
600 }
601
602 /* ----------------
603  *              index_getnext_indexitem - get the next index tuple from a scan
604  *
605  * Finds the next index tuple satisfying the scan keys.  Note that the
606  * corresponding heap tuple is not accessed, and thus no time qual (snapshot)
607  * check is done, other than the index AM's internal check for killed tuples
608  * (which most callers of this routine will probably want to suppress by
609  * setting scan->ignore_killed_tuples = false).
610  *
611  * On success (TRUE return), the found index TID is in scan->currentItemData,
612  * and its heap TID is in scan->xs_ctup.t_self.  scan->xs_cbuf is untouched.
613  * ----------------
614  */
615 bool
616 index_getnext_indexitem(IndexScanDesc scan,
617                                                 ScanDirection direction)
618 {
619         FmgrInfo   *procedure;
620         bool            found;
621
622         SCAN_CHECKS;
623         GET_SCAN_PROCEDURE(amgettuple);
624
625         /* just make sure this is false... */
626         scan->kill_prior_tuple = false;
627
628         /*
629          * have the am's gettuple proc do all the work.
630          */
631         found = DatumGetBool(FunctionCall2(procedure,
632                                                                            PointerGetDatum(scan),
633                                                                            Int32GetDatum(direction)));
634
635         if (found)
636                 pgstat_count_index_tuples(&scan->xs_pgstat_info, 1);
637
638         return found;
639 }
640
641 /* ----------------
642  *              index_getmulti - get multiple tuples from an index scan
643  *
644  * Collects the TIDs of multiple heap tuples satisfying the scan keys.
645  * Since there's no interlock between the index scan and the eventual heap
646  * access, this is only safe to use with MVCC-based snapshots: the heap
647  * item slot could have been replaced by a newer tuple by the time we get
648  * to it.
649  *
650  * A TRUE result indicates more calls should occur; a FALSE result says the
651  * scan is done.  *returned_tids could be zero or nonzero in either case.
652  * ----------------
653  */
654 bool
655 index_getmulti(IndexScanDesc scan,
656                            ItemPointer tids, int32 max_tids,
657                            int32 *returned_tids)
658 {
659         FmgrInfo   *procedure;
660         bool            found;
661
662         SCAN_CHECKS;
663         GET_SCAN_PROCEDURE(amgetmulti);
664
665         /* just make sure this is false... */
666         scan->kill_prior_tuple = false;
667
668         /*
669          * have the am's getmulti proc do all the work.
670          */
671         found = DatumGetBool(FunctionCall4(procedure,
672                                                                            PointerGetDatum(scan),
673                                                                            PointerGetDatum(tids),
674                                                                            Int32GetDatum(max_tids),
675                                                                            PointerGetDatum(returned_tids)));
676
677         pgstat_count_index_tuples(&scan->xs_pgstat_info, *returned_tids);
678
679         return found;
680 }
681
682 /* ----------------
683  *              index_bulk_delete - do mass deletion of index entries
684  *
685  *              callback routine tells whether a given main-heap tuple is
686  *              to be deleted
687  *
688  *              return value is an optional palloc'd struct of statistics
689  * ----------------
690  */
691 IndexBulkDeleteResult *
692 index_bulk_delete(Relation indexRelation,
693                                   IndexBulkDeleteCallback callback,
694                                   void *callback_state)
695 {
696         FmgrInfo   *procedure;
697         IndexBulkDeleteResult *result;
698
699         RELATION_CHECKS;
700         GET_REL_PROCEDURE(ambulkdelete);
701
702         result = (IndexBulkDeleteResult *)
703                 DatumGetPointer(FunctionCall3(procedure,
704                                                                           PointerGetDatum(indexRelation),
705                                                                           PointerGetDatum((Pointer) callback),
706                                                                           PointerGetDatum(callback_state)));
707
708         return result;
709 }
710
711 /* ----------------
712  *              index_vacuum_cleanup - do post-deletion cleanup of an index
713  *
714  *              return value is an optional palloc'd struct of statistics
715  * ----------------
716  */
717 IndexBulkDeleteResult *
718 index_vacuum_cleanup(Relation indexRelation,
719                                          IndexVacuumCleanupInfo *info,
720                                          IndexBulkDeleteResult *stats)
721 {
722         FmgrInfo   *procedure;
723         IndexBulkDeleteResult *result;
724
725         RELATION_CHECKS;
726
727         /* It's okay for an index AM not to have a vacuumcleanup procedure */
728         if (!RegProcedureIsValid(indexRelation->rd_am->amvacuumcleanup))
729                 return stats;
730
731         GET_REL_PROCEDURE(amvacuumcleanup);
732
733         result = (IndexBulkDeleteResult *)
734                 DatumGetPointer(FunctionCall3(procedure,
735                                                                           PointerGetDatum(indexRelation),
736                                                                           PointerGetDatum((Pointer) info),
737                                                                           PointerGetDatum((Pointer) stats)));
738
739         return result;
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 IndexSupportInitialize.
824                  * This should not happen unless the system tables contain bogus
825                  * entries for the index opclass.  (If an AM wants to allow a support
826                  * function to be optional, it can use index_getprocid.)
827                  */
828                 if (!RegProcedureIsValid(procId))
829                         elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
830                                  procnum, attnum, RelationGetRelationName(irel));
831
832                 fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
833         }
834
835         return locinfo;
836 }