1 : /*-------------------------------------------------------------------------
2 : *
3 : * indexam.c
4 : * general index access method routines
5 : *
6 : * Portions Copyright (c) 1996-2008, 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.100 2007/11/15 21:14:32 momjian 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 "access/transam.h"
68 : #include "pgstat.h"
69 : #include "utils/relcache.h"
70 :
71 :
72 : /* ----------------------------------------------------------------
73 : * macros used in index_ routines
74 : * ----------------------------------------------------------------
75 : */
76 : #define RELATION_CHECKS \
77 : ( \
78 : AssertMacro(RelationIsValid(indexRelation)), \
79 : AssertMacro(PointerIsValid(indexRelation->rd_am)) \
80 : )
81 :
82 : #define SCAN_CHECKS \
83 : ( \
84 : AssertMacro(IndexScanIsValid(scan)), \
85 : AssertMacro(RelationIsValid(scan->indexRelation)), \
86 : AssertMacro(PointerIsValid(scan->indexRelation->rd_am)) \
87 : )
88 :
89 : #define GET_REL_PROCEDURE(pname) \
90 : do { \
91 : procedure = &indexRelation->rd_aminfo->pname; \
92 : if (!OidIsValid(procedure->fn_oid)) \
93 : { \
94 : RegProcedure procOid = indexRelation->rd_am->pname; \
95 : if (!RegProcedureIsValid(procOid)) \
96 : elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
97 : fmgr_info_cxt(procOid, procedure, indexRelation->rd_indexcxt); \
98 : } \
99 : } while(0)
100 :
101 : #define GET_SCAN_PROCEDURE(pname) \
102 : do { \
103 : procedure = &scan->indexRelation->rd_aminfo->pname; \
104 : if (!OidIsValid(procedure->fn_oid)) \
105 : { \
106 : RegProcedure procOid = scan->indexRelation->rd_am->pname; \
107 : if (!RegProcedureIsValid(procOid)) \
108 : elog(ERROR, "invalid %s regproc", CppAsString(pname)); \
109 : fmgr_info_cxt(procOid, procedure, scan->indexRelation->rd_indexcxt); \
110 : } \
111 : } while(0)
112 :
113 : static IndexScanDesc index_beginscan_internal(Relation indexRelation,
114 : int nkeys, ScanKey key);
115 :
116 :
117 : /* ----------------------------------------------------------------
118 : * index_ interface functions
119 : * ----------------------------------------------------------------
120 : */
121 :
122 : /* ----------------
123 : * index_open - open an index relation by relation OID
124 : *
125 : * If lockmode is not "NoLock", the specified kind of lock is
126 : * obtained on the index. (Generally, NoLock should only be
127 : * used if the caller knows it has some appropriate lock on the
128 : * index already.)
129 : *
130 : * An error is raised if the index does not exist.
131 : *
132 : * This is a convenience routine adapted for indexscan use.
133 : * Some callers may prefer to use relation_open directly.
134 : * ----------------
135 : */
136 : Relation
137 : index_open(Oid relationId, LOCKMODE lockmode)
138 155852 : {
139 : Relation r;
140 :
141 155852 : r = relation_open(relationId, lockmode);
142 :
143 155852 : if (r->rd_rel->relkind != RELKIND_INDEX)
144 0 : ereport(ERROR,
145 : (errcode(ERRCODE_WRONG_OBJECT_TYPE),
146 : errmsg("\"%s\" is not an index",
147 : RelationGetRelationName(r))));
148 :
149 155852 : return r;
150 : }
151 :
152 : /* ----------------
153 : * index_close - close an index relation
154 : *
155 : * If lockmode is not "NoLock", we then release the specified lock.
156 : *
157 : * Note that it is often sensible to hold a lock beyond index_close;
158 : * in that case, the lock is released automatically at xact end.
159 : * ----------------
160 : */
161 : void
162 : index_close(Relation relation, LOCKMODE lockmode)
163 156299 : {
164 156299 : LockRelId relid = relation->rd_lockInfo.lockRelId;
165 :
166 : Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES);
167 :
168 : /* The relcache does the real work... */
169 156299 : RelationClose(relation);
170 :
171 156299 : if (lockmode != NoLock)
172 147585 : UnlockRelationId(&relid, lockmode);
173 156299 : }
174 :
175 : /* ----------------
176 : * index_insert - insert an index tuple into a relation
177 : * ----------------
178 : */
179 : bool
180 : index_insert(Relation indexRelation,
181 : Datum *values,
182 : bool *isnull,
183 : ItemPointer heap_t_ctid,
184 : Relation heapRelation,
185 : bool check_uniqueness)
186 70122 : {
187 : FmgrInfo *procedure;
188 :
189 : RELATION_CHECKS;
190 70122 : GET_REL_PROCEDURE(aminsert);
191 :
192 : /*
193 : * have the am's insert proc do all the work.
194 : */
195 70122 : return DatumGetBool(FunctionCall6(procedure,
196 : PointerGetDatum(indexRelation),
197 : PointerGetDatum(values),
198 : PointerGetDatum(isnull),
199 : PointerGetDatum(heap_t_ctid),
200 : PointerGetDatum(heapRelation),
201 : BoolGetDatum(check_uniqueness)));
202 : }
203 :
204 : /*
205 : * index_beginscan - start a scan of an index with amgettuple
206 : *
207 : * Note: heapRelation may be NULL if there is no intention of calling
208 : * index_getnext on this scan; index_getnext_indexitem will not use the
209 : * heapRelation link (nor the snapshot). However, the caller had better
210 : * be holding some kind of lock on the heap relation in any case, to ensure
211 : * no one deletes it (or the index) out from under us. Caller must also
212 : * be holding a lock on the index.
213 : */
214 : IndexScanDesc
215 : index_beginscan(Relation heapRelation,
216 : Relation indexRelation,
217 : Snapshot snapshot,
218 : int nkeys, ScanKey key)
219 118332 : {
220 : IndexScanDesc scan;
221 :
222 118332 : scan = index_beginscan_internal(indexRelation, nkeys, key);
223 :
224 : /*
225 : * Save additional parameters into the scandesc. Everything else was set
226 : * up by RelationGetIndexScan.
227 : */
228 118332 : scan->is_multiscan = false;
229 118332 : scan->heapRelation = heapRelation;
230 118332 : scan->xs_snapshot = snapshot;
231 :
232 118332 : return scan;
233 : }
234 :
235 : /*
236 : * index_beginscan_multi - start a scan of an index with amgetmulti
237 : *
238 : * As above, caller had better be holding some lock on the parent heap
239 : * relation, even though it's not explicitly mentioned here.
240 : */
241 : IndexScanDesc
242 : index_beginscan_multi(Relation indexRelation,
243 : Snapshot snapshot,
244 : int nkeys, ScanKey key)
245 196 : {
246 : IndexScanDesc scan;
247 :
248 196 : scan = index_beginscan_internal(indexRelation, nkeys, key);
249 :
250 : /*
251 : * Save additional parameters into the scandesc. Everything else was set
252 : * up by RelationGetIndexScan.
253 : */
254 196 : scan->is_multiscan = true;
255 196 : scan->xs_snapshot = snapshot;
256 :
257 196 : return scan;
258 : }
259 :
260 : /*
261 : * index_beginscan_internal --- common code for index_beginscan variants
262 : */
263 : static IndexScanDesc
264 : index_beginscan_internal(Relation indexRelation,
265 : int nkeys, ScanKey key)
266 118528 : {
267 : IndexScanDesc scan;
268 : FmgrInfo *procedure;
269 :
270 : RELATION_CHECKS;
271 118528 : GET_REL_PROCEDURE(ambeginscan);
272 :
273 : /*
274 : * We hold a reference count to the relcache entry throughout the scan.
275 : */
276 118528 : RelationIncrementReferenceCount(indexRelation);
277 :
278 : /*
279 : * Tell the AM to open a scan.
280 : */
281 118528 : scan = (IndexScanDesc)
282 : DatumGetPointer(FunctionCall3(procedure,
283 : PointerGetDatum(indexRelation),
284 : Int32GetDatum(nkeys),
285 : PointerGetDatum(key)));
286 :
287 118528 : return scan;
288 : }
289 :
290 : /* ----------------
291 : * index_rescan - (re)start a scan of an index
292 : *
293 : * The caller may specify a new set of scankeys (but the number of keys
294 : * cannot change). To restart the scan without changing keys, pass NULL
295 : * for the key array.
296 : *
297 : * Note that this is also called when first starting an indexscan;
298 : * see RelationGetIndexScan. Keys *must* be passed in that case,
299 : * unless scan->numberOfKeys is zero.
300 : * ----------------
301 : */
302 : void
303 : index_rescan(IndexScanDesc scan, ScanKey key)
304 167861 : {
305 : FmgrInfo *procedure;
306 :
307 : SCAN_CHECKS;
308 167861 : GET_SCAN_PROCEDURE(amrescan);
309 :
310 : /* Release any held pin on a heap page */
311 167861 : if (BufferIsValid(scan->xs_cbuf))
312 : {
313 26340 : ReleaseBuffer(scan->xs_cbuf);
314 26340 : scan->xs_cbuf = InvalidBuffer;
315 : }
316 :
317 167861 : scan->xs_next_hot = InvalidOffsetNumber;
318 :
319 167861 : scan->kill_prior_tuple = false; /* for safety */
320 :
321 167861 : FunctionCall2(procedure,
322 : PointerGetDatum(scan),
323 : PointerGetDatum(key));
324 167861 : }
325 :
326 : /* ----------------
327 : * index_endscan - end a scan
328 : * ----------------
329 : */
330 : void
331 : index_endscan(IndexScanDesc scan)
332 118502 : {
333 : FmgrInfo *procedure;
334 :
335 : SCAN_CHECKS;
336 118502 : GET_SCAN_PROCEDURE(amendscan);
337 :
338 : /* Release any held pin on a heap page */
339 118502 : if (BufferIsValid(scan->xs_cbuf))
340 : {
341 70075 : ReleaseBuffer(scan->xs_cbuf);
342 70075 : scan->xs_cbuf = InvalidBuffer;
343 : }
344 :
345 : /* End the AM's scan */
346 118502 : FunctionCall1(procedure, PointerGetDatum(scan));
347 :
348 : /* Release index refcount acquired by index_beginscan */
349 118502 : RelationDecrementReferenceCount(scan->indexRelation);
350 :
351 : /* Release the scan data structure itself */
352 118502 : IndexScanEnd(scan);
353 118502 : }
354 :
355 : /* ----------------
356 : * index_markpos - mark a scan position
357 : * ----------------
358 : */
359 : void
360 : index_markpos(IndexScanDesc scan)
361 0 : {
362 : FmgrInfo *procedure;
363 :
364 : SCAN_CHECKS;
365 0 : GET_SCAN_PROCEDURE(ammarkpos);
366 :
367 0 : FunctionCall1(procedure, PointerGetDatum(scan));
368 0 : }
369 :
370 : /* ----------------
371 : * index_restrpos - restore a scan position
372 : *
373 : * NOTE: this only restores the internal scan state of the index AM.
374 : * The current result tuple (scan->xs_ctup) doesn't change. See comments
375 : * for ExecRestrPos().
376 : *
377 : * NOTE: in the presence of HOT chains, mark/restore only works correctly
378 : * if the scan's snapshot is MVCC-safe; that ensures that there's at most one
379 : * returnable tuple in each HOT chain, and so restoring the prior state at the
380 : * granularity of the index AM is sufficient. Since the only current user
381 : * of mark/restore functionality is nodeMergejoin.c, this effectively means
382 : * that merge-join plans only work for MVCC snapshots. This could be fixed
383 : * if necessary, but for now it seems unimportant.
384 : * ----------------
385 : */
386 : void
387 : index_restrpos(IndexScanDesc scan)
388 0 : {
389 : FmgrInfo *procedure;
390 :
391 : Assert(IsMVCCSnapshot(scan->xs_snapshot));
392 :
393 : SCAN_CHECKS;
394 0 : GET_SCAN_PROCEDURE(amrestrpos);
395 :
396 0 : scan->xs_next_hot = InvalidOffsetNumber;
397 :
398 0 : scan->kill_prior_tuple = false; /* for safety */
399 :
400 0 : FunctionCall1(procedure, PointerGetDatum(scan));
401 0 : }
402 :
403 : /* ----------------
404 : * index_getnext - get the next heap tuple from a scan
405 : *
406 : * The result is the next heap tuple satisfying the scan keys and the
407 : * snapshot, or NULL if no more matching tuples exist. On success,
408 : * the buffer containing the heap tuple is pinned (the pin will be dropped
409 : * at the next index_getnext or index_endscan).
410 : * ----------------
411 : */
412 : HeapTuple
413 : index_getnext(IndexScanDesc scan, ScanDirection direction)
414 240936 : {
415 240936 : HeapTuple heapTuple = &scan->xs_ctup;
416 240936 : ItemPointer tid = &heapTuple->t_self;
417 : FmgrInfo *procedure;
418 :
419 : SCAN_CHECKS;
420 240936 : GET_SCAN_PROCEDURE(amgettuple);
421 :
422 : /*
423 : * We always reset xs_hot_dead; if we are here then either we are just
424 : * starting the scan, or we previously returned a visible tuple, and in
425 : * either case it's inappropriate to kill the prior index entry.
426 : */
427 240936 : scan->xs_hot_dead = false;
428 :
429 : for (;;)
430 : {
431 : OffsetNumber offnum;
432 : bool at_chain_start;
433 : Page dp;
434 :
435 248965 : if (scan->xs_next_hot != InvalidOffsetNumber)
436 : {
437 : /*
438 : * We are resuming scan of a HOT chain after having returned an
439 : * earlier member. Must still hold pin on current heap page.
440 : */
441 : Assert(BufferIsValid(scan->xs_cbuf));
442 : Assert(ItemPointerGetBlockNumber(tid) ==
443 : BufferGetBlockNumber(scan->xs_cbuf));
444 : Assert(TransactionIdIsValid(scan->xs_prev_xmax));
445 0 : offnum = scan->xs_next_hot;
446 0 : at_chain_start = false;
447 0 : scan->xs_next_hot = InvalidOffsetNumber;
448 : }
449 : else
450 : {
451 : bool found;
452 : Buffer prev_buf;
453 :
454 : /*
455 : * If we scanned a whole HOT chain and found only dead tuples,
456 : * tell index AM to kill its entry for that TID.
457 : */
458 248965 : scan->kill_prior_tuple = scan->xs_hot_dead;
459 :
460 : /*
461 : * The AM's gettuple proc finds the next index entry matching the
462 : * scan keys, and puts the TID in xs_ctup.t_self (ie, *tid).
463 : */
464 248965 : found = DatumGetBool(FunctionCall2(procedure,
465 : PointerGetDatum(scan),
466 : Int32GetDatum(direction)));
467 :
468 : /* Reset kill flag immediately for safety */
469 248965 : scan->kill_prior_tuple = false;
470 :
471 : /* If we're out of index entries, break out of outer loop */
472 248965 : if (!found)
473 66754 : break;
474 :
475 182211 : pgstat_count_index_tuples(scan->indexRelation, 1);
476 :
477 : /* Switch to correct buffer if we don't have it already */
478 182211 : prev_buf = scan->xs_cbuf;
479 182211 : scan->xs_cbuf = ReleaseAndReadBuffer(scan->xs_cbuf,
480 : scan->heapRelation,
481 : ItemPointerGetBlockNumber(tid));
482 :
483 : /*
484 : * Prune page, but only if we weren't already on this page
485 : */
486 182211 : if (prev_buf != scan->xs_cbuf)
487 121855 : heap_page_prune_opt(scan->heapRelation, scan->xs_cbuf,
488 : RecentGlobalXmin);
489 :
490 : /* Prepare to scan HOT chain starting at index-referenced offnum */
491 182211 : offnum = ItemPointerGetOffsetNumber(tid);
492 182211 : at_chain_start = true;
493 :
494 : /* We don't know what the first tuple's xmin should be */
495 182211 : scan->xs_prev_xmax = InvalidTransactionId;
496 :
497 : /* Initialize flag to detect if all entries are dead */
498 182211 : scan->xs_hot_dead = true;
499 : }
500 :
501 : /* Obtain share-lock on the buffer so we can examine visibility */
502 182211 : LockBuffer(scan->xs_cbuf, BUFFER_LOCK_SHARE);
503 :
504 182211 : dp = (Page) BufferGetPage(scan->xs_cbuf);
505 :
506 : /* Scan through possible multiple members of HOT-chain */
507 : for (;;)
508 : {
509 : ItemId lp;
510 : ItemPointer ctid;
511 :
512 : /* check for bogus TID */
513 199396 : if (offnum < FirstOffsetNumber ||
514 : offnum > PageGetMaxOffsetNumber(dp))
515 : break;
516 :
517 199396 : lp = PageGetItemId(dp, offnum);
518 :
519 : /* check for unused, dead, or redirected items */
520 199396 : if (!ItemIdIsNormal(lp))
521 : {
522 : /* We should only see a redirect at start of chain */
523 3140 : if (ItemIdIsRedirected(lp) && at_chain_start)
524 : {
525 : /* Follow the redirect */
526 2502 : offnum = ItemIdGetRedirect(lp);
527 2502 : at_chain_start = false;
528 2502 : continue;
529 : }
530 : /* else must be end of chain */
531 : break;
532 : }
533 :
534 : /*
535 : * We must initialize all of *heapTuple (ie, scan->xs_ctup) since
536 : * it is returned to the executor on success.
537 : */
538 196256 : heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
539 196256 : heapTuple->t_len = ItemIdGetLength(lp);
540 196256 : ItemPointerSetOffsetNumber(tid, offnum);
541 196256 : heapTuple->t_tableOid = RelationGetRelid(scan->heapRelation);
542 196256 : ctid = &heapTuple->t_data->t_ctid;
543 :
544 : /*
545 : * Shouldn't see a HEAP_ONLY tuple at chain start. (This test
546 : * should be unnecessary, since the chain root can't be removed
547 : * while we have pin on the index entry, but let's make it
548 : * anyway.)
549 : */
550 196256 : if (at_chain_start && HeapTupleIsHeapOnly(heapTuple))
551 0 : break;
552 :
553 : /*
554 : * The xmin should match the previous xmax value, else chain is
555 : * broken. (Note: this test is not optional because it protects
556 : * us against the case where the prior chain member's xmax aborted
557 : * since we looked at it.)
558 : */
559 196256 : if (TransactionIdIsValid(scan->xs_prev_xmax) &&
560 : !TransactionIdEquals(scan->xs_prev_xmax,
561 : HeapTupleHeaderGetXmin(heapTuple->t_data)))
562 0 : break;
563 :
564 : /* If it's visible per the snapshot, we must return it */
565 196256 : if (HeapTupleSatisfiesVisibility(heapTuple, scan->xs_snapshot,
566 : scan->xs_cbuf))
567 : {
568 : /*
569 : * If the snapshot is MVCC, we know that it could accept at
570 : * most one member of the HOT chain, so we can skip examining
571 : * any more members. Otherwise, check for continuation of the
572 : * HOT-chain, and set state for next time.
573 : */
574 174182 : if (IsMVCCSnapshot(scan->xs_snapshot))
575 33595 : scan->xs_next_hot = InvalidOffsetNumber;
576 140588 : else if (HeapTupleIsHotUpdated(heapTuple))
577 : {
578 : Assert(ItemPointerGetBlockNumber(ctid) ==
579 : ItemPointerGetBlockNumber(tid));
580 1 : scan->xs_next_hot = ItemPointerGetOffsetNumber(ctid);
581 1 : scan->xs_prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
582 : }
583 : else
584 140586 : scan->xs_next_hot = InvalidOffsetNumber;
585 :
586 174182 : LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
587 :
588 174182 : pgstat_count_heap_fetch(scan->indexRelation);
589 :
590 174182 : return heapTuple;
591 : }
592 :
593 : /*
594 : * If we can't see it, maybe no one else can either. Check to see
595 : * if the tuple is dead to all transactions. If we find that all
596 : * the tuples in the HOT chain are dead, we'll signal the index AM
597 : * to not return that TID on future indexscans.
598 : */
599 22074 : if (scan->xs_hot_dead &&
600 : HeapTupleSatisfiesVacuum(heapTuple->t_data, RecentGlobalXmin,
601 : scan->xs_cbuf) != HEAPTUPLE_DEAD)
602 10079 : scan->xs_hot_dead = false;
603 :
604 : /*
605 : * Check to see if HOT chain continues past this tuple; if so
606 : * fetch the next offnum (we don't bother storing it into
607 : * xs_next_hot, but must store xs_prev_xmax), and loop around.
608 : */
609 22074 : if (HeapTupleIsHotUpdated(heapTuple))
610 : {
611 : Assert(ItemPointerGetBlockNumber(ctid) ==
612 : ItemPointerGetBlockNumber(tid));
613 14683 : offnum = ItemPointerGetOffsetNumber(ctid);
614 14683 : at_chain_start = false;
615 14683 : scan->xs_prev_xmax = HeapTupleHeaderGetXmax(heapTuple->t_data);
616 : }
617 : else
618 : break; /* end of chain */
619 : } /* loop over a single HOT chain */
620 :
621 8029 : LockBuffer(scan->xs_cbuf, BUFFER_LOCK_UNLOCK);
622 :
623 : /* Loop around to ask index AM for another TID */
624 8029 : scan->xs_next_hot = InvalidOffsetNumber;
625 8029 : }
626 :
627 : /* Release any held pin on a heap page */
628 66754 : if (BufferIsValid(scan->xs_cbuf))
629 : {
630 16800 : ReleaseBuffer(scan->xs_cbuf);
631 16800 : scan->xs_cbuf = InvalidBuffer;
632 : }
633 :
634 66754 : return NULL; /* failure exit */
635 : }
636 :
637 : /* ----------------
638 : * index_getnext_indexitem - get the next index tuple from a scan
639 : *
640 : * Finds the next index tuple satisfying the scan keys. Note that the
641 : * corresponding heap tuple is not accessed, and thus no time qual (snapshot)
642 : * check is done, other than the index AM's internal check for killed tuples
643 : * (which most callers of this routine will probably want to suppress by
644 : * setting scan->ignore_killed_tuples = false).
645 : *
646 : * On success (TRUE return), the heap TID of the found index entry is in
647 : * scan->xs_ctup.t_self. scan->xs_cbuf is untouched.
648 : * ----------------
649 : */
650 : bool
651 : index_getnext_indexitem(IndexScanDesc scan,
652 : ScanDirection direction)
653 0 : {
654 : FmgrInfo *procedure;
655 : bool found;
656 :
657 : SCAN_CHECKS;
658 0 : GET_SCAN_PROCEDURE(amgettuple);
659 :
660 : /* just make sure this is false... */
661 0 : scan->kill_prior_tuple = false;
662 :
663 : /*
664 : * have the am's gettuple proc do all the work.
665 : */
666 0 : found = DatumGetBool(FunctionCall2(procedure,
667 : PointerGetDatum(scan),
668 : Int32GetDatum(direction)));
669 :
670 0 : if (found)
671 0 : pgstat_count_index_tuples(scan->indexRelation, 1);
672 :
673 0 : return found;
674 : }
675 :
676 : /* ----------------
677 : * index_getmulti - get multiple tuples from an index scan
678 : *
679 : * Collects the TIDs of multiple heap tuples satisfying the scan keys.
680 : * Since there's no interlock between the index scan and the eventual heap
681 : * access, this is only safe to use with MVCC-based snapshots: the heap
682 : * item slot could have been replaced by a newer tuple by the time we get
683 : * to it.
684 : *
685 : * A TRUE result indicates more calls should occur; a FALSE result says the
686 : * scan is done. *returned_tids could be zero or nonzero in either case.
687 : * ----------------
688 : */
689 : bool
690 : index_getmulti(IndexScanDesc scan,
691 : ItemPointer tids, int32 max_tids,
692 : int32 *returned_tids)
693 353 : {
694 : FmgrInfo *procedure;
695 : bool found;
696 :
697 : SCAN_CHECKS;
698 353 : GET_SCAN_PROCEDURE(amgetmulti);
699 :
700 : /* just make sure this is false... */
701 353 : scan->kill_prior_tuple = false;
702 :
703 : /*
704 : * have the am's getmulti proc do all the work.
705 : */
706 353 : found = DatumGetBool(FunctionCall4(procedure,
707 : PointerGetDatum(scan),
708 : PointerGetDatum(tids),
709 : Int32GetDatum(max_tids),
710 : PointerGetDatum(returned_tids)));
711 :
712 353 : pgstat_count_index_tuples(scan->indexRelation, *returned_tids);
713 :
714 353 : return found;
715 : }
716 :
717 : /* ----------------
718 : * index_bulk_delete - do mass deletion of index entries
719 : *
720 : * callback routine tells whether a given main-heap tuple is
721 : * to be deleted
722 : *
723 : * return value is an optional palloc'd struct of statistics
724 : * ----------------
725 : */
726 : IndexBulkDeleteResult *
727 : index_bulk_delete(IndexVacuumInfo *info,
728 : IndexBulkDeleteResult *stats,
729 : IndexBulkDeleteCallback callback,
730 : void *callback_state)
731 70 : {
732 70 : Relation indexRelation = info->index;
733 : FmgrInfo *procedure;
734 : IndexBulkDeleteResult *result;
735 :
736 : RELATION_CHECKS;
737 70 : GET_REL_PROCEDURE(ambulkdelete);
738 :
739 70 : result = (IndexBulkDeleteResult *)
740 : DatumGetPointer(FunctionCall4(procedure,
741 : PointerGetDatum(info),
742 : PointerGetDatum(stats),
743 : PointerGetDatum((Pointer) callback),
744 : PointerGetDatum(callback_state)));
745 :
746 70 : return result;
747 : }
748 :
749 : /* ----------------
750 : * index_vacuum_cleanup - do post-deletion cleanup of an index
751 : *
752 : * return value is an optional palloc'd struct of statistics
753 : * ----------------
754 : */
755 : IndexBulkDeleteResult *
756 : index_vacuum_cleanup(IndexVacuumInfo *info,
757 : IndexBulkDeleteResult *stats)
758 375 : {
759 375 : Relation indexRelation = info->index;
760 : FmgrInfo *procedure;
761 : IndexBulkDeleteResult *result;
762 :
763 : RELATION_CHECKS;
764 375 : GET_REL_PROCEDURE(amvacuumcleanup);
765 :
766 375 : result = (IndexBulkDeleteResult *)
767 : DatumGetPointer(FunctionCall2(procedure,
768 : PointerGetDatum(info),
769 : PointerGetDatum(stats)));
770 :
771 375 : return result;
772 : }
773 :
774 : /* ----------------
775 : * index_getprocid
776 : *
777 : * Index access methods typically require support routines that are
778 : * not directly the implementation of any WHERE-clause query operator
779 : * and so cannot be kept in pg_amop. Instead, such routines are kept
780 : * in pg_amproc. These registered procedure OIDs are assigned numbers
781 : * according to a convention established by the access method.
782 : * The general index code doesn't know anything about the routines
783 : * involved; it just builds an ordered list of them for
784 : * each attribute on which an index is defined.
785 : *
786 : * As of Postgres 8.3, support routines within an operator family
787 : * are further subdivided by the "left type" and "right type" of the
788 : * query operator(s) that they support. The "default" functions for a
789 : * particular indexed attribute are those with both types equal to
790 : * the index opclass' opcintype (note that this is subtly different
791 : * from the indexed attribute's own type: it may be a binary-compatible
792 : * type instead). Only the default functions are stored in relcache
793 : * entries --- access methods can use the syscache to look up non-default
794 : * functions.
795 : *
796 : * This routine returns the requested default procedure OID for a
797 : * particular indexed attribute.
798 : * ----------------
799 : */
800 : RegProcedure
801 : index_getprocid(Relation irel,
802 : AttrNumber attnum,
803 : uint16 procnum)
804 6 : {
805 : RegProcedure *loc;
806 : int nproc;
807 : int procindex;
808 :
809 6 : nproc = irel->rd_am->amsupport;
810 :
811 : Assert(procnum > 0 && procnum <= (uint16) nproc);
812 :
813 6 : procindex = (nproc * (attnum - 1)) + (procnum - 1);
814 :
815 6 : loc = irel->rd_support;
816 :
817 : Assert(loc != NULL);
818 :
819 6 : return loc[procindex];
820 : }
821 :
822 : /* ----------------
823 : * index_getprocinfo
824 : *
825 : * This routine allows index AMs to keep fmgr lookup info for
826 : * support procs in the relcache. As above, only the "default"
827 : * functions for any particular indexed attribute are cached.
828 : *
829 : * Note: the return value points into cached data that will be lost during
830 : * any relcache rebuild! Therefore, either use the callinfo right away,
831 : * or save it only after having acquired some type of lock on the index rel.
832 : * ----------------
833 : */
834 : FmgrInfo *
835 : index_getprocinfo(Relation irel,
836 : AttrNumber attnum,
837 : uint16 procnum)
838 483790 : {
839 : FmgrInfo *locinfo;
840 : int nproc;
841 : int procindex;
842 :
843 483790 : nproc = irel->rd_am->amsupport;
844 :
845 : Assert(procnum > 0 && procnum <= (uint16) nproc);
846 :
847 483790 : procindex = (nproc * (attnum - 1)) + (procnum - 1);
848 :
849 483790 : locinfo = irel->rd_supportinfo;
850 :
851 : Assert(locinfo != NULL);
852 :
853 483790 : locinfo += procindex;
854 :
855 : /* Initialize the lookup info if first time through */
856 483790 : if (locinfo->fn_oid == InvalidOid)
857 : {
858 8294 : RegProcedure *loc = irel->rd_support;
859 : RegProcedure procId;
860 :
861 : Assert(loc != NULL);
862 :
863 8294 : procId = loc[procindex];
864 :
865 : /*
866 : * Complain if function was not found during IndexSupportInitialize.
867 : * This should not happen unless the system tables contain bogus
868 : * entries for the index opclass. (If an AM wants to allow a support
869 : * function to be optional, it can use index_getprocid.)
870 : */
871 8294 : if (!RegProcedureIsValid(procId))
872 0 : elog(ERROR, "missing support function %d for attribute %d of index \"%s\"",
873 : procnum, attnum, RelationGetRelationName(irel));
874 :
875 8294 : fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt);
876 : }
877 :
878 483790 : return locinfo;
879 : }
|