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