]> granicus.if.org Git - postgresql/blob - src/backend/access/index/indexam.c
Restructure operator classes to allow improved handling of cross-data-type
[postgresql] / src / backend / access / index / indexam.c
1 /*-------------------------------------------------------------------------
2  *
3  * indexam.c
4  *        general index access method routines
5  *
6  * Portions Copyright (c) 1996-2006, 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.96 2006/12/23 00:43:08 tgl Exp $
12  *
13  * INTERFACE ROUTINES
14  *              index_open              - open an index relation by relation OID
15  *              index_close             - close an index relation
16  *              index_beginscan - start a scan of an index with amgettuple
17  *              index_beginscan_multi - start a scan of an index with amgetmulti
18  *              index_rescan    - restart a scan of an index
19  *              index_endscan   - end a scan
20  *              index_insert    - insert an index tuple into a relation
21  *              index_markpos   - mark a scan position
22  *              index_restrpos  - restore a scan position
23  *              index_getnext   - get the next tuple from a scan
24  *              index_getmulti  - get multiple tuples from a scan
25  *              index_bulk_delete       - bulk deletion of index tuples
26  *              index_vacuum_cleanup    - post-deletion cleanup of an index
27  *              index_getprocid - get a support procedure OID
28  *              index_getprocinfo - get a support procedure's lookup info
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 "pgstat.h"
68 #include "utils/relcache.h"
69
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(pname) \
89 do { \
90         procedure = &indexRelation->rd_aminfo->pname; \
91         if (!OidIsValid(procedure->fn_oid)) \
92         { \
93                 RegProcedure    procOid = indexRelation->rd_am->pname; \
94                 if (!RegProcedureIsValid(procOid)) \
95                         elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
96                 fmgr_info_cxt(procOid, procedure, indexRelation->rd_indexcxt); \
97         } \
98 } while(0)
99
100 #define GET_SCAN_PROCEDURE(pname) \
101 do { \
102         procedure = &scan->indexRelation->rd_aminfo->pname; \
103         if (!OidIsValid(procedure->fn_oid)) \
104         { \
105                 RegProcedure    procOid = scan->indexRelation->rd_am->pname; \
106                 if (!RegProcedureIsValid(procOid)) \
107                         elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
108                 fmgr_info_cxt(procOid, procedure, scan->indexRelation->rd_indexcxt); \
109         } \
110 } while(0)
111
112 static IndexScanDesc index_beginscan_internal(Relation indexRelation,
113                                                  int nkeys, ScanKey key);
114
115
116 /* ----------------------------------------------------------------
117  *                                 index_ interface functions
118  * ----------------------------------------------------------------
119  */
120
121 /* ----------------
122  *              index_open - open an index relation by relation OID
123  *
124  *              If lockmode is not "NoLock", the specified kind of lock is
125  *              obtained on the index.  (Generally, NoLock should only be
126  *              used if the caller knows it has some appropriate lock on the
127  *              index already.)
128  *
129  *              An error is raised if the index does not exist.
130  *
131  *              This is a convenience routine adapted for indexscan use.
132  *              Some callers may prefer to use relation_open directly.
133  * ----------------
134  */
135 Relation
136 index_open(Oid relationId, LOCKMODE lockmode)
137 {
138         Relation        r;
139
140         r = relation_open(relationId, lockmode);
141
142         if (r->rd_rel->relkind != RELKIND_INDEX)
143                 ereport(ERROR,
144                                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
145                                  errmsg("\"%s\" is not an index",
146                                                 RelationGetRelationName(r))));
147
148         pgstat_initstats(&r->pgstat_info, r);
149
150         return r;
151 }
152
153 /* ----------------
154  *              index_close - close an index relation
155  *
156  *              If lockmode is not "NoLock", we then release the specified lock.
157  *
158  *              Note that it is often sensible to hold a lock beyond index_close;
159  *              in that case, the lock is released automatically at xact end.
160  * ----------------
161  */
162 void
163 index_close(Relation relation, LOCKMODE lockmode)
164 {
165         LockRelId       relid = relation->rd_lockInfo.lockRelId;
166
167         Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
168
169         /* The relcache does the real work... */
170         RelationClose(relation);
171
172         if (lockmode != NoLock)
173                 UnlockRelationId(&relid, lockmode);
174 }
175
176 /* ----------------
177  *              index_insert - insert an index tuple into a relation
178  * ----------------
179  */
180 bool
181 index_insert(Relation indexRelation,
182                          Datum *values,
183                          bool *isnull,
184                          ItemPointer heap_t_ctid,
185                          Relation heapRelation,
186                          bool check_uniqueness)
187 {
188         FmgrInfo   *procedure;
189
190         RELATION_CHECKS;
191         GET_REL_PROCEDURE(aminsert);
192
193         /*
194          * have the am's insert proc do all the work.
195          */
196         return DatumGetBool(FunctionCall6(procedure,
197                                                                           PointerGetDatum(indexRelation),
198                                                                           PointerGetDatum(values),
199                                                                           PointerGetDatum(isnull),
200                                                                           PointerGetDatum(heap_t_ctid),
201                                                                           PointerGetDatum(heapRelation),
202                                                                           BoolGetDatum(check_uniqueness)));
203 }
204
205 /*
206  * index_beginscan - start a scan of an index with amgettuple
207  *
208  * Note: heapRelation may be NULL if there is no intention of calling
209  * index_getnext on this scan; index_getnext_indexitem will not use the
210  * heapRelation link (nor the snapshot).  However, the caller had better
211  * be holding some kind of lock on the heap relation in any case, to ensure
212  * no one deletes it (or the index) out from under us.  Caller must also
213  * be holding a lock on the index.
214  */
215 IndexScanDesc
216 index_beginscan(Relation heapRelation,
217                                 Relation indexRelation,
218                                 Snapshot snapshot,
219                                 int nkeys, ScanKey key)
220 {
221         IndexScanDesc scan;
222
223         scan = index_beginscan_internal(indexRelation, nkeys, key);
224
225         /*
226          * Save additional parameters into the scandesc.  Everything else was set
227          * up by RelationGetIndexScan.
228          */
229         scan->is_multiscan = false;
230         scan->heapRelation = heapRelation;
231         scan->xs_snapshot = snapshot;
232
233         return scan;
234 }
235
236 /*
237  * index_beginscan_multi - start a scan of an index with amgetmulti
238  *
239  * As above, caller had better be holding some lock on the parent heap
240  * relation, even though it's not explicitly mentioned here.
241  */
242 IndexScanDesc
243 index_beginscan_multi(Relation indexRelation,
244                                           Snapshot snapshot,
245                                           int nkeys, ScanKey key)
246 {
247         IndexScanDesc scan;
248
249         scan = index_beginscan_internal(indexRelation, nkeys, key);
250
251         /*
252          * Save additional parameters into the scandesc.  Everything else was set
253          * up by RelationGetIndexScan.
254          */
255         scan->is_multiscan = true;
256         scan->xs_snapshot = snapshot;
257
258         return scan;
259 }
260
261 /*
262  * index_beginscan_internal --- common code for index_beginscan variants
263  */
264 static IndexScanDesc
265 index_beginscan_internal(Relation indexRelation,
266                                                  int nkeys, ScanKey key)
267 {
268         IndexScanDesc scan;
269         FmgrInfo   *procedure;
270
271         RELATION_CHECKS;
272         GET_REL_PROCEDURE(ambeginscan);
273
274         /*
275          * We hold a reference count to the relcache entry throughout the scan.
276          */
277         RelationIncrementReferenceCount(indexRelation);
278
279         /*
280          * Tell the AM to open a scan.
281          */
282         scan = (IndexScanDesc)
283                 DatumGetPointer(FunctionCall3(procedure,
284                                                                           PointerGetDatum(indexRelation),
285                                                                           Int32GetDatum(nkeys),
286                                                                           PointerGetDatum(key)));
287
288         return scan;
289 }
290
291 /* ----------------
292  *              index_rescan  - (re)start a scan of an index
293  *
294  * The caller may specify a new set of scankeys (but the number of keys
295  * cannot change).      To restart the scan without changing keys, pass NULL
296  * for the key array.
297  *
298  * Note that this is also called when first starting an indexscan;
299  * see RelationGetIndexScan.  Keys *must* be passed in that case,
300  * unless scan->numberOfKeys is zero.
301  * ----------------
302  */
303 void
304 index_rescan(IndexScanDesc scan, ScanKey key)
305 {
306         FmgrInfo   *procedure;
307
308         SCAN_CHECKS;
309         GET_SCAN_PROCEDURE(amrescan);
310
311         /* Release any held pin on a heap page */
312         if (BufferIsValid(scan->xs_cbuf))
313         {
314                 ReleaseBuffer(scan->xs_cbuf);
315                 scan->xs_cbuf = InvalidBuffer;
316         }
317
318         scan->kill_prior_tuple = false;         /* for safety */
319
320         FunctionCall2(procedure,
321                                   PointerGetDatum(scan),
322                                   PointerGetDatum(key));
323 }
324
325 /* ----------------
326  *              index_endscan - end a scan
327  * ----------------
328  */
329 void
330 index_endscan(IndexScanDesc scan)
331 {
332         FmgrInfo   *procedure;
333
334         SCAN_CHECKS;
335         GET_SCAN_PROCEDURE(amendscan);
336
337         /* Release any held pin on a heap page */
338         if (BufferIsValid(scan->xs_cbuf))
339         {
340                 ReleaseBuffer(scan->xs_cbuf);
341                 scan->xs_cbuf = InvalidBuffer;
342         }
343
344         /* End the AM's scan */
345         FunctionCall1(procedure, PointerGetDatum(scan));
346
347         /* Release index refcount acquired by index_beginscan */
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         FmgrInfo   *procedure;
362
363         SCAN_CHECKS;
364         GET_SCAN_PROCEDURE(ammarkpos);
365
366         FunctionCall1(procedure, PointerGetDatum(scan));
367 }
368
369 /* ----------------
370  *              index_restrpos  - restore a scan position
371  *
372  * NOTE: this only restores the internal scan state of the index AM.
373  * The current result tuple (scan->xs_ctup) doesn't change.  See comments
374  * for ExecRestrPos().
375  * ----------------
376  */
377 void
378 index_restrpos(IndexScanDesc scan)
379 {
380         FmgrInfo   *procedure;
381
382         SCAN_CHECKS;
383         GET_SCAN_PROCEDURE(amrestrpos);
384
385         scan->kill_prior_tuple = false;         /* for safety */
386
387         FunctionCall1(procedure, PointerGetDatum(scan));
388 }
389
390 /* ----------------
391  *              index_getnext - get the next heap tuple from a scan
392  *
393  * The result is the next heap tuple satisfying the scan keys and the
394  * snapshot, or NULL if no more matching tuples exist.  On success,
395  * the buffer containing the heap tuple is pinned (the pin will be dropped
396  * at the next index_getnext or index_endscan).
397  * ----------------
398  */
399 HeapTuple
400 index_getnext(IndexScanDesc scan, ScanDirection direction)
401 {
402         HeapTuple       heapTuple = &scan->xs_ctup;
403         FmgrInfo   *procedure;
404
405         SCAN_CHECKS;
406         GET_SCAN_PROCEDURE(amgettuple);
407
408         /* just make sure this is false... */
409         scan->kill_prior_tuple = false;
410
411         for (;;)
412         {
413                 bool            found;
414
415                 /*
416                  * The AM's gettuple proc finds the next tuple matching the scan keys.
417                  */
418                 found = DatumGetBool(FunctionCall2(procedure,
419                                                                                    PointerGetDatum(scan),
420                                                                                    Int32GetDatum(direction)));
421
422                 /* Reset kill flag immediately for safety */
423                 scan->kill_prior_tuple = false;
424
425                 if (!found)
426                 {
427                         /* Release any held pin on a heap page */
428                         if (BufferIsValid(scan->xs_cbuf))
429                         {
430                                 ReleaseBuffer(scan->xs_cbuf);
431                                 scan->xs_cbuf = InvalidBuffer;
432                         }
433                         return NULL;            /* failure exit */
434                 }
435
436                 pgstat_count_index_tuples(&scan->xs_pgstat_info, 1);
437
438                 /*
439                  * Fetch the heap tuple and see if it matches the snapshot.
440                  */
441                 if (heap_release_fetch(scan->heapRelation, scan->xs_snapshot,
442                                                            heapTuple, &scan->xs_cbuf, true,
443                                                            &scan->xs_pgstat_info))
444                         break;
445
446                 /* Skip if no undeleted tuple at this location */
447                 if (heapTuple->t_data == NULL)
448                         continue;
449
450                 /*
451                  * If we can't see it, maybe no one else can either.  Check to see if
452                  * the tuple is dead to all transactions.  If so, signal the index AM
453                  * to not return it on future indexscans.
454                  *
455                  * We told heap_release_fetch to keep a pin on the buffer, so we can
456                  * re-access the tuple here.  But we must re-lock the buffer first.
457                  */
458                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
459
460                 if (HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
461                                                                          scan->xs_cbuf) == HEAPTUPLE_DEAD)
462                         scan->kill_prior_tuple = true;
463
464                 LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
465         }
466
467         /* Success exit */
468         return heapTuple;
469 }
470
471 /* ----------------
472  *              index_getnext_indexitem - get the next index tuple from a scan
473  *
474  * Finds the next index tuple satisfying the scan keys.  Note that the
475  * corresponding heap tuple is not accessed, and thus no time qual (snapshot)
476  * check is done, other than the index AM's internal check for killed tuples
477  * (which most callers of this routine will probably want to suppress by
478  * setting scan->ignore_killed_tuples = false).
479  *
480  * On success (TRUE return), the heap TID of the found index entry is in
481  * scan->xs_ctup.t_self.  scan->xs_cbuf is untouched.
482  * ----------------
483  */
484 bool
485 index_getnext_indexitem(IndexScanDesc scan,
486                                                 ScanDirection direction)
487 {
488         FmgrInfo   *procedure;
489         bool            found;
490
491         SCAN_CHECKS;
492         GET_SCAN_PROCEDURE(amgettuple);
493
494         /* just make sure this is false... */
495         scan->kill_prior_tuple = false;
496
497         /*
498          * have the am's gettuple proc do all the work.
499          */
500         found = DatumGetBool(FunctionCall2(procedure,
501                                                                            PointerGetDatum(scan),
502                                                                            Int32GetDatum(direction)));
503
504         if (found)
505                 pgstat_count_index_tuples(&scan->xs_pgstat_info, 1);
506
507         return found;
508 }
509
510 /* ----------------
511  *              index_getmulti - get multiple tuples from an index scan
512  *
513  * Collects the TIDs of multiple heap tuples satisfying the scan keys.
514  * Since there's no interlock between the index scan and the eventual heap
515  * access, this is only safe to use with MVCC-based snapshots: the heap
516  * item slot could have been replaced by a newer tuple by the time we get
517  * to it.
518  *
519  * A TRUE result indicates more calls should occur; a FALSE result says the
520  * scan is done.  *returned_tids could be zero or nonzero in either case.
521  * ----------------
522  */
523 bool
524 index_getmulti(IndexScanDesc scan,
525                            ItemPointer tids, int32 max_tids,
526                            int32 *returned_tids)
527 {
528         FmgrInfo   *procedure;
529         bool            found;
530
531         SCAN_CHECKS;
532         GET_SCAN_PROCEDURE(amgetmulti);
533
534         /* just make sure this is false... */
535         scan->kill_prior_tuple = false;
536
537         /*
538          * have the am's getmulti proc do all the work.
539          */
540         found = DatumGetBool(FunctionCall4(procedure,
541                                                                            PointerGetDatum(scan),
542                                                                            PointerGetDatum(tids),
543                                                                            Int32GetDatum(max_tids),
544                                                                            PointerGetDatum(returned_tids)));
545
546         pgstat_count_index_tuples(&scan->xs_pgstat_info, *returned_tids);
547
548         return found;
549 }
550
551 /* ----------------
552  *              index_bulk_delete - do mass deletion of index entries
553  *
554  *              callback routine tells whether a given main-heap tuple is
555  *              to be deleted
556  *
557  *              return value is an optional palloc'd struct of statistics
558  * ----------------
559  */
560 IndexBulkDeleteResult *
561 index_bulk_delete(IndexVacuumInfo *info,
562                                   IndexBulkDeleteResult *stats,
563                                   IndexBulkDeleteCallback callback,
564                                   void *callback_state)
565 {
566         Relation        indexRelation = info->index;
567         FmgrInfo   *procedure;
568         IndexBulkDeleteResult *result;
569
570         RELATION_CHECKS;
571         GET_REL_PROCEDURE(ambulkdelete);
572
573         result = (IndexBulkDeleteResult *)
574                 DatumGetPointer(FunctionCall4(procedure,
575                                                                           PointerGetDatum(info),
576                                                                           PointerGetDatum(stats),
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(IndexVacuumInfo *info,
591                                          IndexBulkDeleteResult *stats)
592 {
593         Relation        indexRelation = info->index;
594         FmgrInfo   *procedure;
595         IndexBulkDeleteResult *result;
596
597         RELATION_CHECKS;
598         GET_REL_PROCEDURE(amvacuumcleanup);
599
600         result = (IndexBulkDeleteResult *)
601                 DatumGetPointer(FunctionCall2(procedure,
602                                                                           PointerGetDatum(info),
603                                                                           PointerGetDatum(stats)));
604
605         return result;
606 }
607
608 /* ----------------
609  *              index_getprocid
610  *
611  *              Index access methods typically require support routines that are
612  *              not directly the implementation of any WHERE-clause query operator
613  *              and so cannot be kept in pg_amop.  Instead, such routines are kept
614  *              in pg_amproc.  These registered procedure OIDs are assigned numbers
615  *              according to a convention established by the access method.
616  *              The general index code doesn't know anything about the routines
617  *              involved; it just builds an ordered list of them for
618  *              each attribute on which an index is defined.
619  *
620  *              As of Postgres 8.3, support routines within an operator family
621  *              are further subdivided by the "left type" and "right type" of the
622  *              query operator(s) that they support.  The "default" functions for a
623  *              particular indexed attribute are those with both types equal to
624  *              the index opclass' opcintype (note that this is subtly different
625  *              from the indexed attribute's own type: it may be a binary-compatible
626  *              type instead).  Only the default functions are stored in relcache
627  *              entries --- access methods can use the syscache to look up non-default
628  *              functions.
629  *
630  *              This routine returns the requested default procedure OID for a
631  *              particular indexed attribute.
632  * ----------------
633  */
634 RegProcedure
635 index_getprocid(Relation irel,
636                                 AttrNumber attnum,
637                                 uint16 procnum)
638 {
639         RegProcedure *loc;
640         int                     nproc;
641         int                     procindex;
642
643         nproc = irel->rd_am->amsupport;
644
645         Assert(procnum > 0 && procnum <= (uint16) nproc);
646
647         procindex = (nproc * (attnum - 1)) + (procnum - 1);
648
649         loc = irel->rd_support;
650
651         Assert(loc != NULL);
652
653         return loc[procindex];
654 }
655
656 /* ----------------
657  *              index_getprocinfo
658  *
659  *              This routine allows index AMs to keep fmgr lookup info for
660  *              support procs in the relcache.  As above, only the "default"
661  *              functions for any particular indexed attribute are cached.
662  *
663  * Note: the return value points into cached data that will be lost during
664  * any relcache rebuild!  Therefore, either use the callinfo right away,
665  * or save it only after having acquired some type of lock on the index rel.
666  * ----------------
667  */
668 FmgrInfo *
669 index_getprocinfo(Relation irel,
670                                   AttrNumber attnum,
671                                   uint16 procnum)
672 {
673         FmgrInfo   *locinfo;
674         int                     nproc;
675         int                     procindex;
676
677         nproc = irel->rd_am->amsupport;
678
679         Assert(procnum > 0 && procnum <= (uint16) nproc);
680
681         procindex = (nproc * (attnum - 1)) + (procnum - 1);
682
683         locinfo = irel->rd_supportinfo;
684
685         Assert(locinfo != NULL);
686
687         locinfo += procindex;
688
689         /* Initialize the lookup info if first time through */
690         if (locinfo->fn_oid == InvalidOid)
691         {
692                 RegProcedure *loc = irel->rd_support;
693                 RegProcedure procId;
694
695                 Assert(loc != NULL);
696
697                 procId = loc[procindex];
698
699                 /*
700                  * Complain if function was not found during IndexSupportInitialize.
701                  * This should not happen unless the system tables contain bogus
702                  * entries for the index opclass.  (If an AM wants to allow a support
703                  * function to be optional, it can use index_getprocid.)
704                  */
705                 if (!RegProcedureIsValid(procId))
706                         elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
707                                  procnum, attnum, RelationGetRelationName(irel));
708
709                 fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
710         }
711
712         return locinfo;
713 }