]> granicus.if.org Git - postgresql/blob - src/backend/access/index/indexam.c
Revise handling of index-type-specific indexscan cost estimation, per
[postgresql] / src / backend / access / index / indexam.c
1 /*-------------------------------------------------------------------------
2  *
3  * indexam.c
4  *        general index access method routines
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/access/index/indexam.c,v 1.39 2000/01/22 23:50:09 tgl Exp $
11  *
12  * INTERFACE ROUTINES
13  *              index_open              - open an index relation by relationId
14  *              index_openr             - open a index relation by name
15  *              index_close             - close a index relation
16  *              index_beginscan - start a scan of an index
17  *              index_rescan    - restart a scan of an index
18  *              index_endscan   - end a scan
19  *              index_insert    - insert an index tuple into a relation
20  *              index_delete    - delete an item from an index 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_fetch             - retrieve tuple with tid
25  * **   index_replace   - replace a tuple
26  * **   index_getattr   - get an attribute from an index tuple
27  *              index_getprocid - get a support procedure id from the rel tuple
28  *
29  *              IndexScanIsValid - check index scan
30  *
31  * NOTES
32  *              This file contains the index_ routines which used
33  *              to be a scattered collection of stuff in access/genam.
34  *
35  *              The ** routines: index_fetch, index_replace, and index_getattr
36  *              have not yet been implemented.  They may not be needed.
37  *
38  * old comments
39  *              Scans are implemented as follows:
40  *
41  *              `0' represents an invalid item pointer.
42  *              `-' represents an unknown item pointer.
43  *              `X' represents a known item pointers.
44  *              `+' represents known or invalid item pointers.
45  *              `*' represents any item pointers.
46  *
47  *              State is represented by a triple of these symbols in the order of
48  *              previous, current, next.  Note that the case of reverse scans works
49  *              identically.
50  *
51  *                              State   Result
52  *              (1)             + + -   + 0 0                   (if the next item pointer is invalid)
53  *              (2)                             + X -                   (otherwise)
54  *              (3)             * 0 0   * 0 0                   (no change)
55  *              (4)             + X 0   X 0 0                   (shift)
56  *              (5)             * + X   + X -                   (shift, add unknown)
57  *
58  *              All other states cannot occur.
59  *
60  *              Note: It would be possible to cache the status of the previous and
61  *                        next item pointer using the flags.
62  *
63  *-------------------------------------------------------------------------
64  */
65
66 #include "postgres.h"
67
68 #include "access/genam.h"
69 #include "access/heapam.h"
70 #include "utils/relcache.h"
71
72
73 /* ----------------------------------------------------------------
74  *                                      macros used in index_ routines
75  * ----------------------------------------------------------------
76  */
77 #define RELATION_CHECKS \
78 ( \
79         AssertMacro(RelationIsValid(relation)), \
80         AssertMacro(PointerIsValid(relation->rd_am)) \
81 )
82
83 #define SCAN_CHECKS \
84 ( \
85         AssertMacro(IndexScanIsValid(scan)), \
86         AssertMacro(RelationIsValid(scan->relation)), \
87         AssertMacro(PointerIsValid(scan->relation->rd_am)) \
88 )
89
90 #define GET_REL_PROCEDURE(x,y) \
91 ( \
92         procedure = relation->rd_am->y, \
93         (!RegProcedureIsValid(procedure)) ? \
94                 elog(ERROR, "index_%s: invalid %s regproc", \
95                         CppAsString(x), CppAsString(y)) \
96         : (void)NULL \
97 )
98
99 #define GET_SCAN_PROCEDURE(x,y) \
100 ( \
101         procedure = scan->relation->rd_am->y, \
102         (!RegProcedureIsValid(procedure)) ? \
103                 elog(ERROR, "index_%s: invalid %s regproc", \
104                         CppAsString(x), CppAsString(y)) \
105         : (void)NULL \
106 )
107
108
109 /* ----------------------------------------------------------------
110  *                                 index_ interface functions
111  * ----------------------------------------------------------------
112  */
113 /* ----------------
114  *              index_open - open an index relation by relationId
115  *
116  *              presently the relcache routines do all the work we need
117  *              to open/close index relations.  However, callers of index_open
118  *              expect it to succeed, so we need to check for a failure return.
119  *
120  *              Note: we acquire no lock on the index.  An AccessShareLock is
121  *              acquired by index_beginscan (and released by index_endscan).
122  * ----------------
123  */
124 Relation
125 index_open(Oid relationId)
126 {
127         Relation        r;
128
129         r = RelationIdGetRelation(relationId);
130
131         if (! RelationIsValid(r))
132                 elog(ERROR, "Index %u does not exist", relationId);
133
134         if (r->rd_rel->relkind != RELKIND_INDEX)
135                 elog(ERROR, "%s is not an index relation", RelationGetRelationName(r));
136
137         return r;
138 }
139
140 /* ----------------
141  *              index_openr - open a index relation by name
142  *
143  *              As above, but lookup by name instead of OID.
144  * ----------------
145  */
146 Relation
147 index_openr(char *relationName)
148 {
149         Relation        r;
150
151         r = RelationNameGetRelation(relationName);
152
153         if (! RelationIsValid(r))
154                 elog(ERROR, "Index '%s' does not exist", relationName);
155
156         if (r->rd_rel->relkind != RELKIND_INDEX)
157                 elog(ERROR, "%s is not an index relation", RelationGetRelationName(r));
158
159         return r;
160 }
161
162 /* ----------------
163  *              index_close - close a index relation
164  *
165  *              presently the relcache routines do all the work we need
166  *              to open/close index relations.
167  * ----------------
168  */
169 void
170 index_close(Relation relation)
171 {
172         RelationClose(relation);
173 }
174
175 /* ----------------
176  *              index_insert - insert an index tuple into a relation
177  * ----------------
178  */
179 InsertIndexResult
180 index_insert(Relation relation,
181                          Datum *datum,
182                          char *nulls,
183                          ItemPointer heap_t_ctid,
184                          Relation heapRel)
185 {
186         RegProcedure procedure;
187         InsertIndexResult specificResult;
188
189         RELATION_CHECKS;
190         GET_REL_PROCEDURE(insert, aminsert);
191
192         /* ----------------
193          *      have the am's insert proc do all the work.
194          * ----------------
195          */
196         specificResult = (InsertIndexResult)
197                 fmgr(procedure, relation, datum, nulls, heap_t_ctid, heapRel, NULL);
198
199         /* must be pfree'ed */
200         return specificResult;
201 }
202
203 /* ----------------
204  *              index_delete - delete an item from an index relation
205  * ----------------
206  */
207 void
208 index_delete(Relation relation, ItemPointer indexItem)
209 {
210         RegProcedure procedure;
211
212         RELATION_CHECKS;
213         GET_REL_PROCEDURE(delete, amdelete);
214
215         fmgr(procedure, relation, indexItem);
216 }
217
218 /* ----------------
219  *              index_beginscan - start a scan of an index
220  * ----------------
221  */
222 IndexScanDesc
223 index_beginscan(Relation relation,
224                                 bool scanFromEnd,
225                                 uint16 numberOfKeys,
226                                 ScanKey key)
227 {
228         IndexScanDesc scandesc;
229         RegProcedure procedure;
230
231         RELATION_CHECKS;
232         GET_REL_PROCEDURE(beginscan, ambeginscan);
233
234         RelationIncrementReferenceCount(relation);
235
236         /* ----------------
237          *      Acquire AccessShareLock for the duration of the scan
238          *
239          *      Note: we could get an SI inval message here and consequently have
240          *      to rebuild the relcache entry.  The refcount increment above
241          *      ensures that we will rebuild it and not just flush it...
242          * ----------------
243          */
244         LockRelation(relation, AccessShareLock);
245
246         scandesc = (IndexScanDesc)
247                 fmgr(procedure, relation, scanFromEnd, numberOfKeys, key);
248
249         return scandesc;
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         fmgr(procedure, scan, scanFromEnd, key);
265 }
266
267 /* ----------------
268  *              index_endscan - end a scan
269  * ----------------
270  */
271 void
272 index_endscan(IndexScanDesc scan)
273 {
274         RegProcedure procedure;
275
276         SCAN_CHECKS;
277         GET_SCAN_PROCEDURE(endscan, amendscan);
278
279         fmgr(procedure, scan);
280
281         /* Release lock and refcount acquired by index_beginscan */
282
283         UnlockRelation(scan->relation, AccessShareLock);
284
285         RelationDecrementReferenceCount(scan->relation);
286
287         /* Release the scan data structure itself */
288         IndexScanEnd(scan);
289 }
290
291 /* ----------------
292  *              index_markpos  - mark a scan position
293  * ----------------
294  */
295 void
296 index_markpos(IndexScanDesc scan)
297 {
298         RegProcedure procedure;
299
300         SCAN_CHECKS;
301         GET_SCAN_PROCEDURE(markpos, ammarkpos);
302
303         fmgr(procedure, scan);
304 }
305
306 /* ----------------
307  *              index_restrpos  - restore a scan position
308  * ----------------
309  */
310 void
311 index_restrpos(IndexScanDesc scan)
312 {
313         RegProcedure procedure;
314
315         SCAN_CHECKS;
316         GET_SCAN_PROCEDURE(restrpos, amrestrpos);
317
318         fmgr(procedure, scan);
319 }
320
321 /* ----------------
322  *              index_getnext - get the next tuple from a scan
323  *
324  *              A RetrieveIndexResult is a index tuple/heap tuple pair
325  * ----------------
326  */
327 RetrieveIndexResult
328 index_getnext(IndexScanDesc scan,
329                           ScanDirection direction)
330 {
331         RegProcedure procedure;
332         RetrieveIndexResult result;
333
334         SCAN_CHECKS;
335         GET_SCAN_PROCEDURE(getnext, amgettuple);
336
337         /* ----------------
338          *      have the am's gettuple proc do all the work.
339          * ----------------
340          */
341         result = (RetrieveIndexResult) fmgr(procedure, scan, direction);
342
343         return result;
344 }
345
346 /* ----------------
347  *              index_cost_estimator
348  *
349  *              Fetch the amcostestimate procedure OID for an index.
350  *
351  *              We could combine fetching and calling the procedure,
352  *              as index_insert does for example; but that would require
353  *              importing a bunch of planner/optimizer stuff into this file.
354  * ----------------
355  */
356 RegProcedure
357 index_cost_estimator(Relation relation)
358 {
359         RegProcedure procedure;
360
361         RELATION_CHECKS;
362         GET_REL_PROCEDURE(cost_estimator, amcostestimate);
363
364         return procedure;
365 }
366
367 /* ----------------
368  *              index_getprocid
369  *
370  *              Some indexed access methods may require support routines that are
371  *              not in the operator class/operator model imposed by pg_am.      These
372  *              access methods may store the OIDs of registered procedures they
373  *              need in pg_amproc.      These registered procedure OIDs are ordered in
374  *              a way that makes sense to the access method, and used only by the
375  *              access method.  The general index code doesn't know anything about
376  *              the routines involved; it just builds an ordered list of them for
377  *              each attribute on which an index is defined.
378  *
379  *              This routine returns the requested procedure OID for a particular
380  *              indexed attribute.
381  * ----------------
382  */
383 RegProcedure
384 index_getprocid(Relation irel,
385                                 AttrNumber attnum,
386                                 uint16 procnum)
387 {
388         RegProcedure *loc;
389         int                     natts;
390
391         natts = irel->rd_rel->relnatts;
392
393         loc = irel->rd_support;
394
395         Assert(loc != NULL);
396
397         return loc[(natts * (procnum - 1)) + (attnum - 1)];
398 }
399
400 Datum
401 GetIndexValue(HeapTuple tuple,
402                           TupleDesc hTupDesc,
403                           int attOff,
404                           AttrNumber *attrNums,
405                           FuncIndexInfo *fInfo,
406                           bool *attNull)
407 {
408         Datum           returnVal;
409         bool            isNull = FALSE;
410
411         if (PointerIsValid(fInfo) && FIgetProcOid(fInfo) != InvalidOid)
412         {
413                 int                     i;
414                 Datum      *attData = (Datum *) palloc(FIgetnArgs(fInfo) * sizeof(Datum));
415
416                 for (i = 0; i < FIgetnArgs(fInfo); i++)
417                 {
418                         attData[i] = heap_getattr(tuple,
419                                                                           attrNums[i],
420                                                                           hTupDesc,
421                                                                           attNull);
422                         if (*attNull)
423                                 isNull = TRUE;
424                 }
425                 returnVal = (Datum) fmgr_array_args(FIgetProcOid(fInfo),
426                                                                                         FIgetnArgs(fInfo),
427                                                                                         (char **) attData,
428                                                                                         &isNull);
429                 pfree(attData);
430                 *attNull = isNull;
431         }
432         else
433                 returnVal = heap_getattr(tuple, attrNums[attOff], hTupDesc, attNull);
434
435         return returnVal;
436 }