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