]> granicus.if.org Git - postgresql/blob - src/backend/access/index/indexam.c
ce2c8e62f45cd7b3134f299c5915decd515101f2
[postgresql] / src / backend / access / index / indexam.c
1 /*-------------------------------------------------------------------------
2  *
3  * indexam.c
4  *        general index access method routines
5  *
6  * Portions Copyright (c) 1996-2001, 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.57 2002/04/17 20:57:56 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_cost_estimator    - fetch amcostestimate procedure OID
27  *              index_getprocid - get a support procedure OID
28  *
29  * NOTES
30  *              This file contains the index_ routines which used
31  *              to be a scattered collection of stuff in access/genam.
32  *
33  *
34  * old comments
35  *              Scans are implemented as follows:
36  *
37  *              `0' represents an invalid item pointer.
38  *              `-' represents an unknown item pointer.
39  *              `X' represents a known item pointers.
40  *              `+' represents known or invalid item pointers.
41  *              `*' represents any item pointers.
42  *
43  *              State is represented by a triple of these symbols in the order of
44  *              previous, current, next.  Note that the case of reverse scans works
45  *              identically.
46  *
47  *                              State   Result
48  *              (1)             + + -   + 0 0                   (if the next item pointer is invalid)
49  *              (2)                             + X -                   (otherwise)
50  *              (3)             * 0 0   * 0 0                   (no change)
51  *              (4)             + X 0   X 0 0                   (shift)
52  *              (5)             * + X   + X -                   (shift, add unknown)
53  *
54  *              All other states cannot occur.
55  *
56  *              Note: It would be possible to cache the status of the previous and
57  *                        next item pointer using the flags.
58  *
59  *-------------------------------------------------------------------------
60  */
61
62 #include "postgres.h"
63
64 #include "access/genam.h"
65 #include "access/heapam.h"
66 #include "utils/relcache.h"
67
68 #include "pgstat.h"
69
70 /* ----------------------------------------------------------------
71  *                                      macros used in index_ routines
72  * ----------------------------------------------------------------
73  */
74 #define RELATION_CHECKS \
75 ( \
76         AssertMacro(RelationIsValid(relation)), \
77         AssertMacro(PointerIsValid(relation->rd_am)) \
78 )
79
80 #define SCAN_CHECKS \
81 ( \
82         AssertMacro(IndexScanIsValid(scan)), \
83         AssertMacro(RelationIsValid(scan->relation)), \
84         AssertMacro(PointerIsValid(scan->relation->rd_am)) \
85 )
86
87 #define GET_REL_PROCEDURE(x,y) \
88 ( \
89         procedure = relation->rd_am->y, \
90         (!RegProcedureIsValid(procedure)) ? \
91                 elog(ERROR, "index_%s: invalid %s regproc", \
92                         CppAsString(x), CppAsString(y)) \
93         : (void)NULL \
94 )
95
96 #define GET_SCAN_PROCEDURE(x,y) \
97 ( \
98         procedure = scan->relation->rd_am->y, \
99         (!RegProcedureIsValid(procedure)) ? \
100                 elog(ERROR, "index_%s: invalid %s regproc", \
101                         CppAsString(x), CppAsString(y)) \
102         : (void)NULL \
103 )
104
105
106 /* ----------------------------------------------------------------
107  *                                 index_ interface functions
108  * ----------------------------------------------------------------
109  */
110
111 /* ----------------
112  *              index_open - open an index relation by relation OID
113  *
114  *              Note: we acquire no lock on the index.  An AccessShareLock is
115  *              acquired by index_beginscan (and released by index_endscan).
116  *              Generally, the caller should already hold some type of lock on
117  *              the parent relation to ensure that the index doesn't disappear.
118  *
119  *              This is a convenience routine adapted for indexscan use.
120  *              Some callers may prefer to use relation_open directly.
121  * ----------------
122  */
123 Relation
124 index_open(Oid relationId)
125 {
126         Relation        r;
127
128         r = relation_open(relationId, NoLock);
129
130         if (r->rd_rel->relkind != RELKIND_INDEX)
131                 elog(ERROR, "%s is not an index relation",
132                          RelationGetRelationName(r));
133
134         pgstat_initstats(&r->pgstat_info, r);
135
136         return r;
137 }
138
139 /* ----------------
140  *              index_openrv - open an index relation specified
141  *              by a RangeVar node
142  *
143  *              As above, but relation is specified by a RangeVar.
144  * ----------------
145  */
146 Relation
147 index_openrv(const RangeVar *relation)
148 {
149         Relation        r;
150
151         r = relation_openrv(relation, NoLock);
152
153         if (r->rd_rel->relkind != RELKIND_INDEX)
154                 elog(ERROR, "%s is not an index relation",
155                          RelationGetRelationName(r));
156
157         pgstat_initstats(&r->pgstat_info, r);
158
159         return r;
160 }
161
162 /* ----------------
163  *              index_openr - open a system index relation specified by name.
164  *
165  *              As above, but the relation is specified by an unqualified name;
166  *              it is assumed to live in the system catalog namespace.
167  * ----------------
168  */
169 Relation
170 index_openr(const char *sysRelationName)
171 {
172         Relation        r;
173
174         r = relation_openr(sysRelationName, NoLock);
175
176         if (r->rd_rel->relkind != RELKIND_INDEX)
177                 elog(ERROR, "%s is not an index relation",
178                          RelationGetRelationName(r));
179
180         pgstat_initstats(&r->pgstat_info, r);
181
182         return r;
183 }
184
185 /* ----------------
186  *              index_close - close a index relation
187  *
188  *              presently the relcache routines do all the work we need
189  *              to open/close index relations.
190  * ----------------
191  */
192 void
193 index_close(Relation relation)
194 {
195         RelationClose(relation);
196 }
197
198 /* ----------------
199  *              index_insert - insert an index tuple into a relation
200  * ----------------
201  */
202 InsertIndexResult
203 index_insert(Relation relation,
204                          Datum *datum,
205                          char *nulls,
206                          ItemPointer heap_t_ctid,
207                          Relation heapRel)
208 {
209         RegProcedure procedure;
210         InsertIndexResult specificResult;
211
212         RELATION_CHECKS;
213         GET_REL_PROCEDURE(insert, aminsert);
214
215         /*
216          * have the am's insert proc do all the work.
217          */
218         specificResult = (InsertIndexResult)
219                 DatumGetPointer(OidFunctionCall5(procedure,
220                                                                                  PointerGetDatum(relation),
221                                                                                  PointerGetDatum(datum),
222                                                                                  PointerGetDatum(nulls),
223                                                                                  PointerGetDatum(heap_t_ctid),
224                                                                                  PointerGetDatum(heapRel)));
225
226         /* must be pfree'ed */
227         return specificResult;
228 }
229
230 /* ----------------
231  *              index_beginscan - start a scan of an index
232  * ----------------
233  */
234 IndexScanDesc
235 index_beginscan(Relation relation,
236                                 bool scanFromEnd,
237                                 uint16 numberOfKeys,
238                                 ScanKey key)
239 {
240         IndexScanDesc scan;
241         RegProcedure procedure;
242
243         RELATION_CHECKS;
244         GET_REL_PROCEDURE(beginscan, ambeginscan);
245
246         RelationIncrementReferenceCount(relation);
247
248         /*
249          * Acquire AccessShareLock for the duration of the scan
250          *
251          * Note: we could get an SI inval message here and consequently have to
252          * rebuild the relcache entry.  The refcount increment above ensures
253          * that we will rebuild it and not just flush it...
254          */
255         LockRelation(relation, AccessShareLock);
256
257         scan = (IndexScanDesc)
258                 DatumGetPointer(OidFunctionCall4(procedure,
259                                                                                  PointerGetDatum(relation),
260                                                                                  BoolGetDatum(scanFromEnd),
261                                                                                  UInt16GetDatum(numberOfKeys),
262                                                                                  PointerGetDatum(key)));
263
264         pgstat_initstats(&scan->xs_pgstat_info, relation);
265
266         /*
267          * We want to look up the amgettuple procedure just once per scan, not
268          * once per index_getnext call.  So do it here and save the fmgr info
269          * result in the scan descriptor.
270          */
271         GET_SCAN_PROCEDURE(beginscan, amgettuple);
272         fmgr_info(procedure, &scan->fn_getnext);
273
274         return scan;
275 }
276
277 /* ----------------
278  *              index_rescan  - restart a scan of an index
279  * ----------------
280  */
281 void
282 index_rescan(IndexScanDesc scan, bool scanFromEnd, ScanKey key)
283 {
284         RegProcedure procedure;
285
286         SCAN_CHECKS;
287         GET_SCAN_PROCEDURE(rescan, amrescan);
288
289         OidFunctionCall3(procedure,
290                                          PointerGetDatum(scan),
291                                          BoolGetDatum(scanFromEnd),
292                                          PointerGetDatum(key));
293
294         pgstat_reset_index_scan(&scan->xs_pgstat_info);
295 }
296
297 /* ----------------
298  *              index_endscan - end a scan
299  * ----------------
300  */
301 void
302 index_endscan(IndexScanDesc scan)
303 {
304         RegProcedure procedure;
305
306         SCAN_CHECKS;
307         GET_SCAN_PROCEDURE(endscan, amendscan);
308
309         OidFunctionCall1(procedure, PointerGetDatum(scan));
310
311         /* Release lock and refcount acquired by index_beginscan */
312
313         UnlockRelation(scan->relation, AccessShareLock);
314
315         RelationDecrementReferenceCount(scan->relation);
316
317         /* Release the scan data structure itself */
318         IndexScanEnd(scan);
319 }
320
321 /* ----------------
322  *              index_markpos  - mark a scan position
323  * ----------------
324  */
325 void
326 index_markpos(IndexScanDesc scan)
327 {
328         RegProcedure procedure;
329
330         SCAN_CHECKS;
331         GET_SCAN_PROCEDURE(markpos, ammarkpos);
332
333         OidFunctionCall1(procedure, PointerGetDatum(scan));
334 }
335
336 /* ----------------
337  *              index_restrpos  - restore a scan position
338  * ----------------
339  */
340 void
341 index_restrpos(IndexScanDesc scan)
342 {
343         RegProcedure procedure;
344
345         SCAN_CHECKS;
346         GET_SCAN_PROCEDURE(restrpos, amrestrpos);
347
348         OidFunctionCall1(procedure, PointerGetDatum(scan));
349 }
350
351 /* ----------------
352  *              index_getnext - get the next tuple from a scan
353  *
354  *              A RetrieveIndexResult is a index tuple/heap tuple pair
355  * ----------------
356  */
357 RetrieveIndexResult
358 index_getnext(IndexScanDesc scan,
359                           ScanDirection direction)
360 {
361         RetrieveIndexResult result;
362
363         SCAN_CHECKS;
364
365         pgstat_count_index_scan(&scan->xs_pgstat_info);
366
367         /*
368          * have the am's gettuple proc do all the work. index_beginscan
369          * already set up fn_getnext.
370          */
371         result = (RetrieveIndexResult)
372                 DatumGetPointer(FunctionCall2(&scan->fn_getnext,
373                                                                           PointerGetDatum(scan),
374                                                                           Int32GetDatum(direction)));
375
376         if (result != NULL)
377                 pgstat_count_index_getnext(&scan->xs_pgstat_info);
378         return result;
379 }
380
381 /* ----------------
382  *              index_bulk_delete - do mass deletion of index entries
383  *
384  *              callback routine tells whether a given main-heap tuple is
385  *              to be deleted
386  *
387  *              return value is an optional palloc'd struct of statistics
388  * ----------------
389  */
390 IndexBulkDeleteResult *
391 index_bulk_delete(Relation relation,
392                                   IndexBulkDeleteCallback callback,
393                                   void *callback_state)
394 {
395         RegProcedure procedure;
396         IndexBulkDeleteResult *result;
397
398         RELATION_CHECKS;
399         GET_REL_PROCEDURE(bulk_delete, ambulkdelete);
400
401         result = (IndexBulkDeleteResult *)
402                 DatumGetPointer(OidFunctionCall3(procedure,
403                                                                                  PointerGetDatum(relation),
404                                                                          PointerGetDatum((Pointer) callback),
405                                                                            PointerGetDatum(callback_state)));
406
407         return result;
408 }
409
410 /* ----------------
411  *              index_cost_estimator
412  *
413  *              Fetch the amcostestimate procedure OID for an index.
414  *
415  *              We could combine fetching and calling the procedure,
416  *              as index_insert does for example; but that would require
417  *              importing a bunch of planner/optimizer stuff into this file.
418  * ----------------
419  */
420 RegProcedure
421 index_cost_estimator(Relation relation)
422 {
423         RegProcedure procedure;
424
425         RELATION_CHECKS;
426         GET_REL_PROCEDURE(cost_estimator, amcostestimate);
427
428         return procedure;
429 }
430
431 /* ----------------
432  *              index_getprocid
433  *
434  *              Some indexed access methods may require support routines that are
435  *              not in the operator class/operator model imposed by pg_am.      These
436  *              access methods may store the OIDs of registered procedures they
437  *              need in pg_amproc.      These registered procedure OIDs are ordered in
438  *              a way that makes sense to the access method, and used only by the
439  *              access method.  The general index code doesn't know anything about
440  *              the routines involved; it just builds an ordered list of them for
441  *              each attribute on which an index is defined.
442  *
443  *              This routine returns the requested procedure OID for a particular
444  *              indexed attribute.
445  * ----------------
446  */
447 RegProcedure
448 index_getprocid(Relation irel,
449                                 AttrNumber attnum,
450                                 uint16 procnum)
451 {
452         RegProcedure *loc;
453         int                     nproc;
454         int                     procindex;
455
456         nproc = irel->rd_am->amsupport;
457
458         Assert(procnum > 0 && procnum <= (uint16) nproc);
459
460         procindex = (nproc * (attnum - 1)) + (procnum - 1);
461
462         loc = irel->rd_support;
463
464         Assert(loc != NULL);
465
466         return loc[procindex];
467 }
468
469 /* ----------------
470  *              index_getprocinfo
471  *
472  *              This routine allows index AMs to keep fmgr lookup info for
473  *              support procs in the relcache.
474  * ----------------
475  */
476 struct FmgrInfo *
477 index_getprocinfo(Relation irel,
478                                   AttrNumber attnum,
479                                   uint16 procnum)
480 {
481         FmgrInfo   *locinfo;
482         int                     nproc;
483         int                     procindex;
484
485         nproc = irel->rd_am->amsupport;
486
487         Assert(procnum > 0 && procnum <= (uint16) nproc);
488
489         procindex = (nproc * (attnum - 1)) + (procnum - 1);
490
491         locinfo = irel->rd_supportinfo;
492
493         Assert(locinfo != NULL);
494
495         locinfo += procindex;
496
497         /* Initialize the lookup info if first time through */
498         if (locinfo->fn_oid == InvalidOid)
499         {
500                 RegProcedure *loc = irel->rd_support;
501                 RegProcedure procId;
502
503                 Assert(loc != NULL);
504
505                 procId = loc[procindex];
506
507                 /*
508                  * Complain if function was not found during IndexSupportInitialize.
509                  * This should not happen unless the system tables contain bogus
510                  * entries for the index opclass.  (If an AM wants to allow a
511                  * support function to be optional, it can use index_getprocid.)
512                  */
513                 if (!RegProcedureIsValid(procId))
514                         elog(ERROR, "Missing support function %d for attribute %d of index %s",
515                                  procnum, attnum, RelationGetRelationName(irel));
516
517                 fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
518         }
519
520         return locinfo;
521 }