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