1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * indexam.c |
4 | * general index access method routines |
5 | * |
6 | * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group |
7 | * Portions Copyright (c) 1994, Regents of the University of California |
8 | * |
9 | * |
10 | * IDENTIFICATION |
11 | * src/backend/access/index/indexam.c |
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_bitmap - start a scan of an index with amgetbitmap |
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_parallelscan_estimate - estimate shared memory for parallel scan |
24 | * index_parallelscan_initialize - initialize parallel scan |
25 | * index_parallelrescan - (re)start a parallel scan of an index |
26 | * index_beginscan_parallel - join parallel index scan |
27 | * index_getnext_tid - get the next TID from a scan |
28 | * index_fetch_heap - get the scan's next heap tuple |
29 | * index_getnext_slot - get the next tuple from a scan |
30 | * index_getbitmap - get all tuples from a scan |
31 | * index_bulk_delete - bulk deletion of index tuples |
32 | * index_vacuum_cleanup - post-deletion cleanup of an index |
33 | * index_can_return - does index support index-only scans? |
34 | * index_getprocid - get a support procedure OID |
35 | * index_getprocinfo - get a support procedure's lookup info |
36 | * |
37 | * NOTES |
38 | * This file contains the index_ routines which used |
39 | * to be a scattered collection of stuff in access/genam. |
40 | * |
41 | *------------------------------------------------------------------------- |
42 | */ |
43 | |
44 | #include "postgres.h" |
45 | |
46 | #include "access/amapi.h" |
47 | #include "access/heapam.h" |
48 | #include "access/relscan.h" |
49 | #include "access/tableam.h" |
50 | #include "access/transam.h" |
51 | #include "access/xlog.h" |
52 | #include "catalog/index.h" |
53 | #include "catalog/pg_type.h" |
54 | #include "pgstat.h" |
55 | #include "storage/bufmgr.h" |
56 | #include "storage/lmgr.h" |
57 | #include "storage/predicate.h" |
58 | #include "utils/snapmgr.h" |
59 | |
60 | |
61 | /* ---------------------------------------------------------------- |
62 | * macros used in index_ routines |
63 | * |
64 | * Note: the ReindexIsProcessingIndex() check in RELATION_CHECKS is there |
65 | * to check that we don't try to scan or do retail insertions into an index |
66 | * that is currently being rebuilt or pending rebuild. This helps to catch |
67 | * things that don't work when reindexing system catalogs. The assertion |
68 | * doesn't prevent the actual rebuild because we don't use RELATION_CHECKS |
69 | * when calling the index AM's ambuild routine, and there is no reason for |
70 | * ambuild to call its subsidiary routines through this file. |
71 | * ---------------------------------------------------------------- |
72 | */ |
73 | #define RELATION_CHECKS \ |
74 | ( \ |
75 | AssertMacro(RelationIsValid(indexRelation)), \ |
76 | AssertMacro(PointerIsValid(indexRelation->rd_indam)), \ |
77 | AssertMacro(!ReindexIsProcessingIndex(RelationGetRelid(indexRelation))) \ |
78 | ) |
79 | |
80 | #define SCAN_CHECKS \ |
81 | ( \ |
82 | AssertMacro(IndexScanIsValid(scan)), \ |
83 | AssertMacro(RelationIsValid(scan->indexRelation)), \ |
84 | AssertMacro(PointerIsValid(scan->indexRelation->rd_indam)) \ |
85 | ) |
86 | |
87 | #define CHECK_REL_PROCEDURE(pname) \ |
88 | do { \ |
89 | if (indexRelation->rd_indam->pname == NULL) \ |
90 | elog(ERROR, "function %s is not defined for index %s", \ |
91 | CppAsString(pname), RelationGetRelationName(indexRelation)); \ |
92 | } while(0) |
93 | |
94 | #define CHECK_SCAN_PROCEDURE(pname) \ |
95 | do { \ |
96 | if (scan->indexRelation->rd_indam->pname == NULL) \ |
97 | elog(ERROR, "function %s is not defined for index %s", \ |
98 | CppAsString(pname), RelationGetRelationName(scan->indexRelation)); \ |
99 | } while(0) |
100 | |
101 | static IndexScanDesc index_beginscan_internal(Relation indexRelation, |
102 | int nkeys, int norderbys, Snapshot snapshot, |
103 | ParallelIndexScanDesc pscan, bool temp_snap); |
104 | |
105 | |
106 | /* ---------------------------------------------------------------- |
107 | * index_ interface functions |
108 | * ---------------------------------------------------------------- |
109 | */ |
110 | |
111 | /* ---------------- |
112 | * index_open - open an index relation by relation OID |
113 | * |
114 | * If lockmode is not "NoLock", the specified kind of lock is |
115 | * obtained on the index. (Generally, NoLock should only be |
116 | * used if the caller knows it has some appropriate lock on the |
117 | * index already.) |
118 | * |
119 | * An error is raised if the index does not exist. |
120 | * |
121 | * This is a convenience routine adapted for indexscan use. |
122 | * Some callers may prefer to use relation_open directly. |
123 | * ---------------- |
124 | */ |
125 | Relation |
126 | index_open(Oid relationId, LOCKMODE lockmode) |
127 | { |
128 | Relation r; |
129 | |
130 | r = relation_open(relationId, lockmode); |
131 | |
132 | if (r->rd_rel->relkind != RELKIND_INDEX && |
133 | r->rd_rel->relkind != RELKIND_PARTITIONED_INDEX) |
134 | ereport(ERROR, |
135 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
136 | errmsg("\"%s\" is not an index" , |
137 | RelationGetRelationName(r)))); |
138 | |
139 | return r; |
140 | } |
141 | |
142 | /* ---------------- |
143 | * index_close - close an index relation |
144 | * |
145 | * If lockmode is not "NoLock", we then release the specified lock. |
146 | * |
147 | * Note that it is often sensible to hold a lock beyond index_close; |
148 | * in that case, the lock is released automatically at xact end. |
149 | * ---------------- |
150 | */ |
151 | void |
152 | index_close(Relation relation, LOCKMODE lockmode) |
153 | { |
154 | LockRelId relid = relation->rd_lockInfo.lockRelId; |
155 | |
156 | Assert(lockmode >= NoLock && lockmode < MAX_LOCKMODES); |
157 | |
158 | /* The relcache does the real work... */ |
159 | RelationClose(relation); |
160 | |
161 | if (lockmode != NoLock) |
162 | UnlockRelationId(&relid, lockmode); |
163 | } |
164 | |
165 | /* ---------------- |
166 | * index_insert - insert an index tuple into a relation |
167 | * ---------------- |
168 | */ |
169 | bool |
170 | index_insert(Relation indexRelation, |
171 | Datum *values, |
172 | bool *isnull, |
173 | ItemPointer heap_t_ctid, |
174 | Relation heapRelation, |
175 | IndexUniqueCheck checkUnique, |
176 | IndexInfo *indexInfo) |
177 | { |
178 | RELATION_CHECKS; |
179 | CHECK_REL_PROCEDURE(aminsert); |
180 | |
181 | if (!(indexRelation->rd_indam->ampredlocks)) |
182 | CheckForSerializableConflictIn(indexRelation, |
183 | (HeapTuple) NULL, |
184 | InvalidBuffer); |
185 | |
186 | return indexRelation->rd_indam->aminsert(indexRelation, values, isnull, |
187 | heap_t_ctid, heapRelation, |
188 | checkUnique, indexInfo); |
189 | } |
190 | |
191 | /* |
192 | * index_beginscan - start a scan of an index with amgettuple |
193 | * |
194 | * Caller must be holding suitable locks on the heap and the index. |
195 | */ |
196 | IndexScanDesc |
197 | index_beginscan(Relation heapRelation, |
198 | Relation indexRelation, |
199 | Snapshot snapshot, |
200 | int nkeys, int norderbys) |
201 | { |
202 | IndexScanDesc scan; |
203 | |
204 | scan = index_beginscan_internal(indexRelation, nkeys, norderbys, snapshot, NULL, false); |
205 | |
206 | /* |
207 | * Save additional parameters into the scandesc. Everything else was set |
208 | * up by RelationGetIndexScan. |
209 | */ |
210 | scan->heapRelation = heapRelation; |
211 | scan->xs_snapshot = snapshot; |
212 | |
213 | /* prepare to fetch index matches from table */ |
214 | scan->xs_heapfetch = table_index_fetch_begin(heapRelation); |
215 | |
216 | return scan; |
217 | } |
218 | |
219 | /* |
220 | * index_beginscan_bitmap - start a scan of an index with amgetbitmap |
221 | * |
222 | * As above, caller had better be holding some lock on the parent heap |
223 | * relation, even though it's not explicitly mentioned here. |
224 | */ |
225 | IndexScanDesc |
226 | index_beginscan_bitmap(Relation indexRelation, |
227 | Snapshot snapshot, |
228 | int nkeys) |
229 | { |
230 | IndexScanDesc scan; |
231 | |
232 | scan = index_beginscan_internal(indexRelation, nkeys, 0, snapshot, NULL, false); |
233 | |
234 | /* |
235 | * Save additional parameters into the scandesc. Everything else was set |
236 | * up by RelationGetIndexScan. |
237 | */ |
238 | scan->xs_snapshot = snapshot; |
239 | |
240 | return scan; |
241 | } |
242 | |
243 | /* |
244 | * index_beginscan_internal --- common code for index_beginscan variants |
245 | */ |
246 | static IndexScanDesc |
247 | index_beginscan_internal(Relation indexRelation, |
248 | int nkeys, int norderbys, Snapshot snapshot, |
249 | ParallelIndexScanDesc pscan, bool temp_snap) |
250 | { |
251 | IndexScanDesc scan; |
252 | |
253 | RELATION_CHECKS; |
254 | CHECK_REL_PROCEDURE(ambeginscan); |
255 | |
256 | if (!(indexRelation->rd_indam->ampredlocks)) |
257 | PredicateLockRelation(indexRelation, snapshot); |
258 | |
259 | /* |
260 | * We hold a reference count to the relcache entry throughout the scan. |
261 | */ |
262 | RelationIncrementReferenceCount(indexRelation); |
263 | |
264 | /* |
265 | * Tell the AM to open a scan. |
266 | */ |
267 | scan = indexRelation->rd_indam->ambeginscan(indexRelation, nkeys, |
268 | norderbys); |
269 | /* Initialize information for parallel scan. */ |
270 | scan->parallel_scan = pscan; |
271 | scan->xs_temp_snap = temp_snap; |
272 | |
273 | return scan; |
274 | } |
275 | |
276 | /* ---------------- |
277 | * index_rescan - (re)start a scan of an index |
278 | * |
279 | * During a restart, the caller may specify a new set of scankeys and/or |
280 | * orderbykeys; but the number of keys cannot differ from what index_beginscan |
281 | * was told. (Later we might relax that to "must not exceed", but currently |
282 | * the index AMs tend to assume that scan->numberOfKeys is what to believe.) |
283 | * To restart the scan without changing keys, pass NULL for the key arrays. |
284 | * (Of course, keys *must* be passed on the first call, unless |
285 | * scan->numberOfKeys is zero.) |
286 | * ---------------- |
287 | */ |
288 | void |
289 | index_rescan(IndexScanDesc scan, |
290 | ScanKey keys, int nkeys, |
291 | ScanKey orderbys, int norderbys) |
292 | { |
293 | SCAN_CHECKS; |
294 | CHECK_SCAN_PROCEDURE(amrescan); |
295 | |
296 | Assert(nkeys == scan->numberOfKeys); |
297 | Assert(norderbys == scan->numberOfOrderBys); |
298 | |
299 | /* Release resources (like buffer pins) from table accesses */ |
300 | if (scan->xs_heapfetch) |
301 | table_index_fetch_reset(scan->xs_heapfetch); |
302 | |
303 | scan->kill_prior_tuple = false; /* for safety */ |
304 | scan->xs_heap_continue = false; |
305 | |
306 | scan->indexRelation->rd_indam->amrescan(scan, keys, nkeys, |
307 | orderbys, norderbys); |
308 | } |
309 | |
310 | /* ---------------- |
311 | * index_endscan - end a scan |
312 | * ---------------- |
313 | */ |
314 | void |
315 | index_endscan(IndexScanDesc scan) |
316 | { |
317 | SCAN_CHECKS; |
318 | CHECK_SCAN_PROCEDURE(amendscan); |
319 | |
320 | /* Release resources (like buffer pins) from table accesses */ |
321 | if (scan->xs_heapfetch) |
322 | { |
323 | table_index_fetch_end(scan->xs_heapfetch); |
324 | scan->xs_heapfetch = NULL; |
325 | } |
326 | |
327 | /* End the AM's scan */ |
328 | scan->indexRelation->rd_indam->amendscan(scan); |
329 | |
330 | /* Release index refcount acquired by index_beginscan */ |
331 | RelationDecrementReferenceCount(scan->indexRelation); |
332 | |
333 | if (scan->xs_temp_snap) |
334 | UnregisterSnapshot(scan->xs_snapshot); |
335 | |
336 | /* Release the scan data structure itself */ |
337 | IndexScanEnd(scan); |
338 | } |
339 | |
340 | /* ---------------- |
341 | * index_markpos - mark a scan position |
342 | * ---------------- |
343 | */ |
344 | void |
345 | index_markpos(IndexScanDesc scan) |
346 | { |
347 | SCAN_CHECKS; |
348 | CHECK_SCAN_PROCEDURE(ammarkpos); |
349 | |
350 | scan->indexRelation->rd_indam->ammarkpos(scan); |
351 | } |
352 | |
353 | /* ---------------- |
354 | * index_restrpos - restore a scan position |
355 | * |
356 | * NOTE: this only restores the internal scan state of the index AM. See |
357 | * comments for ExecRestrPos(). |
358 | * |
359 | * NOTE: For heap, in the presence of HOT chains, mark/restore only works |
360 | * correctly if the scan's snapshot is MVCC-safe; that ensures that there's at |
361 | * most one returnable tuple in each HOT chain, and so restoring the prior |
362 | * state at the granularity of the index AM is sufficient. Since the only |
363 | * current user of mark/restore functionality is nodeMergejoin.c, this |
364 | * effectively means that merge-join plans only work for MVCC snapshots. This |
365 | * could be fixed if necessary, but for now it seems unimportant. |
366 | * ---------------- |
367 | */ |
368 | void |
369 | index_restrpos(IndexScanDesc scan) |
370 | { |
371 | Assert(IsMVCCSnapshot(scan->xs_snapshot)); |
372 | |
373 | SCAN_CHECKS; |
374 | CHECK_SCAN_PROCEDURE(amrestrpos); |
375 | |
376 | /* release resources (like buffer pins) from table accesses */ |
377 | if (scan->xs_heapfetch) |
378 | table_index_fetch_reset(scan->xs_heapfetch); |
379 | |
380 | scan->kill_prior_tuple = false; /* for safety */ |
381 | scan->xs_heap_continue = false; |
382 | |
383 | scan->indexRelation->rd_indam->amrestrpos(scan); |
384 | } |
385 | |
386 | /* |
387 | * index_parallelscan_estimate - estimate shared memory for parallel scan |
388 | * |
389 | * Currently, we don't pass any information to the AM-specific estimator, |
390 | * so it can probably only return a constant. In the future, we might need |
391 | * to pass more information. |
392 | */ |
393 | Size |
394 | index_parallelscan_estimate(Relation indexRelation, Snapshot snapshot) |
395 | { |
396 | Size nbytes; |
397 | |
398 | RELATION_CHECKS; |
399 | |
400 | nbytes = offsetof(ParallelIndexScanDescData, ps_snapshot_data); |
401 | nbytes = add_size(nbytes, EstimateSnapshotSpace(snapshot)); |
402 | nbytes = MAXALIGN(nbytes); |
403 | |
404 | /* |
405 | * If amestimateparallelscan is not provided, assume there is no |
406 | * AM-specific data needed. (It's hard to believe that could work, but |
407 | * it's easy enough to cater to it here.) |
408 | */ |
409 | if (indexRelation->rd_indam->amestimateparallelscan != NULL) |
410 | nbytes = add_size(nbytes, |
411 | indexRelation->rd_indam->amestimateparallelscan()); |
412 | |
413 | return nbytes; |
414 | } |
415 | |
416 | /* |
417 | * index_parallelscan_initialize - initialize parallel scan |
418 | * |
419 | * We initialize both the ParallelIndexScanDesc proper and the AM-specific |
420 | * information which follows it. |
421 | * |
422 | * This function calls access method specific initialization routine to |
423 | * initialize am specific information. Call this just once in the leader |
424 | * process; then, individual workers attach via index_beginscan_parallel. |
425 | */ |
426 | void |
427 | index_parallelscan_initialize(Relation heapRelation, Relation indexRelation, |
428 | Snapshot snapshot, ParallelIndexScanDesc target) |
429 | { |
430 | Size offset; |
431 | |
432 | RELATION_CHECKS; |
433 | |
434 | offset = add_size(offsetof(ParallelIndexScanDescData, ps_snapshot_data), |
435 | EstimateSnapshotSpace(snapshot)); |
436 | offset = MAXALIGN(offset); |
437 | |
438 | target->ps_relid = RelationGetRelid(heapRelation); |
439 | target->ps_indexid = RelationGetRelid(indexRelation); |
440 | target->ps_offset = offset; |
441 | SerializeSnapshot(snapshot, target->ps_snapshot_data); |
442 | |
443 | /* aminitparallelscan is optional; assume no-op if not provided by AM */ |
444 | if (indexRelation->rd_indam->aminitparallelscan != NULL) |
445 | { |
446 | void *amtarget; |
447 | |
448 | amtarget = OffsetToPointer(target, offset); |
449 | indexRelation->rd_indam->aminitparallelscan(amtarget); |
450 | } |
451 | } |
452 | |
453 | /* ---------------- |
454 | * index_parallelrescan - (re)start a parallel scan of an index |
455 | * ---------------- |
456 | */ |
457 | void |
458 | index_parallelrescan(IndexScanDesc scan) |
459 | { |
460 | SCAN_CHECKS; |
461 | |
462 | if (scan->xs_heapfetch) |
463 | table_index_fetch_reset(scan->xs_heapfetch); |
464 | |
465 | /* amparallelrescan is optional; assume no-op if not provided by AM */ |
466 | if (scan->indexRelation->rd_indam->amparallelrescan != NULL) |
467 | scan->indexRelation->rd_indam->amparallelrescan(scan); |
468 | } |
469 | |
470 | /* |
471 | * index_beginscan_parallel - join parallel index scan |
472 | * |
473 | * Caller must be holding suitable locks on the heap and the index. |
474 | */ |
475 | IndexScanDesc |
476 | index_beginscan_parallel(Relation heaprel, Relation indexrel, int nkeys, |
477 | int norderbys, ParallelIndexScanDesc pscan) |
478 | { |
479 | Snapshot snapshot; |
480 | IndexScanDesc scan; |
481 | |
482 | Assert(RelationGetRelid(heaprel) == pscan->ps_relid); |
483 | snapshot = RestoreSnapshot(pscan->ps_snapshot_data); |
484 | RegisterSnapshot(snapshot); |
485 | scan = index_beginscan_internal(indexrel, nkeys, norderbys, snapshot, |
486 | pscan, true); |
487 | |
488 | /* |
489 | * Save additional parameters into the scandesc. Everything else was set |
490 | * up by index_beginscan_internal. |
491 | */ |
492 | scan->heapRelation = heaprel; |
493 | scan->xs_snapshot = snapshot; |
494 | |
495 | /* prepare to fetch index matches from table */ |
496 | scan->xs_heapfetch = table_index_fetch_begin(heaprel); |
497 | |
498 | return scan; |
499 | } |
500 | |
501 | /* ---------------- |
502 | * index_getnext_tid - get the next TID from a scan |
503 | * |
504 | * The result is the next TID satisfying the scan keys, |
505 | * or NULL if no more matching tuples exist. |
506 | * ---------------- |
507 | */ |
508 | ItemPointer |
509 | index_getnext_tid(IndexScanDesc scan, ScanDirection direction) |
510 | { |
511 | bool found; |
512 | |
513 | SCAN_CHECKS; |
514 | CHECK_SCAN_PROCEDURE(amgettuple); |
515 | |
516 | Assert(TransactionIdIsValid(RecentGlobalXmin)); |
517 | |
518 | /* |
519 | * The AM's amgettuple proc finds the next index entry matching the scan |
520 | * keys, and puts the TID into scan->xs_heaptid. It should also set |
521 | * scan->xs_recheck and possibly scan->xs_itup/scan->xs_hitup, though we |
522 | * pay no attention to those fields here. |
523 | */ |
524 | found = scan->indexRelation->rd_indam->amgettuple(scan, direction); |
525 | |
526 | /* Reset kill flag immediately for safety */ |
527 | scan->kill_prior_tuple = false; |
528 | scan->xs_heap_continue = false; |
529 | |
530 | /* If we're out of index entries, we're done */ |
531 | if (!found) |
532 | { |
533 | /* release resources (like buffer pins) from table accesses */ |
534 | if (scan->xs_heapfetch) |
535 | table_index_fetch_reset(scan->xs_heapfetch); |
536 | |
537 | return NULL; |
538 | } |
539 | Assert(ItemPointerIsValid(&scan->xs_heaptid)); |
540 | |
541 | pgstat_count_index_tuples(scan->indexRelation, 1); |
542 | |
543 | /* Return the TID of the tuple we found. */ |
544 | return &scan->xs_heaptid; |
545 | } |
546 | |
547 | /* ---------------- |
548 | * index_fetch_heap - get the scan's next heap tuple |
549 | * |
550 | * The result is a visible heap tuple associated with the index TID most |
551 | * recently fetched by index_getnext_tid, or NULL if no more matching tuples |
552 | * exist. (There can be more than one matching tuple because of HOT chains, |
553 | * although when using an MVCC snapshot it should be impossible for more than |
554 | * one such tuple to exist.) |
555 | * |
556 | * On success, the buffer containing the heap tup is pinned (the pin will be |
557 | * dropped in a future index_getnext_tid, index_fetch_heap or index_endscan |
558 | * call). |
559 | * |
560 | * Note: caller must check scan->xs_recheck, and perform rechecking of the |
561 | * scan keys if required. We do not do that here because we don't have |
562 | * enough information to do it efficiently in the general case. |
563 | * ---------------- |
564 | */ |
565 | bool |
566 | index_fetch_heap(IndexScanDesc scan, TupleTableSlot *slot) |
567 | { |
568 | bool all_dead = false; |
569 | bool found; |
570 | |
571 | found = table_index_fetch_tuple(scan->xs_heapfetch, &scan->xs_heaptid, |
572 | scan->xs_snapshot, slot, |
573 | &scan->xs_heap_continue, &all_dead); |
574 | |
575 | if (found) |
576 | pgstat_count_heap_fetch(scan->indexRelation); |
577 | |
578 | /* |
579 | * If we scanned a whole HOT chain and found only dead tuples, tell index |
580 | * AM to kill its entry for that TID (this will take effect in the next |
581 | * amgettuple call, in index_getnext_tid). We do not do this when in |
582 | * recovery because it may violate MVCC to do so. See comments in |
583 | * RelationGetIndexScan(). |
584 | */ |
585 | if (!scan->xactStartedInRecovery) |
586 | scan->kill_prior_tuple = all_dead; |
587 | |
588 | return found; |
589 | } |
590 | |
591 | /* ---------------- |
592 | * index_getnext_slot - get the next tuple from a scan |
593 | * |
594 | * The result is true if a tuple satisfying the scan keys and the snapshot was |
595 | * found, false otherwise. The tuple is stored in the specified slot. |
596 | * |
597 | * On success, resources (like buffer pins) are likely to be held, and will be |
598 | * dropped by a future index_getnext_tid, index_fetch_heap or index_endscan |
599 | * call). |
600 | * |
601 | * Note: caller must check scan->xs_recheck, and perform rechecking of the |
602 | * scan keys if required. We do not do that here because we don't have |
603 | * enough information to do it efficiently in the general case. |
604 | * ---------------- |
605 | */ |
606 | bool |
607 | index_getnext_slot(IndexScanDesc scan, ScanDirection direction, TupleTableSlot *slot) |
608 | { |
609 | for (;;) |
610 | { |
611 | if (!scan->xs_heap_continue) |
612 | { |
613 | ItemPointer tid; |
614 | |
615 | /* Time to fetch the next TID from the index */ |
616 | tid = index_getnext_tid(scan, direction); |
617 | |
618 | /* If we're out of index entries, we're done */ |
619 | if (tid == NULL) |
620 | break; |
621 | |
622 | Assert(ItemPointerEquals(tid, &scan->xs_heaptid)); |
623 | } |
624 | |
625 | /* |
626 | * Fetch the next (or only) visible heap tuple for this index entry. |
627 | * If we don't find anything, loop around and grab the next TID from |
628 | * the index. |
629 | */ |
630 | Assert(ItemPointerIsValid(&scan->xs_heaptid)); |
631 | if (index_fetch_heap(scan, slot)) |
632 | return true; |
633 | } |
634 | |
635 | return false; |
636 | } |
637 | |
638 | /* ---------------- |
639 | * index_getbitmap - get all tuples at once from an index scan |
640 | * |
641 | * Adds the TIDs of all heap tuples satisfying the scan keys to a bitmap. |
642 | * Since there's no interlock between the index scan and the eventual heap |
643 | * access, this is only safe to use with MVCC-based snapshots: the heap |
644 | * item slot could have been replaced by a newer tuple by the time we get |
645 | * to it. |
646 | * |
647 | * Returns the number of matching tuples found. (Note: this might be only |
648 | * approximate, so it should only be used for statistical purposes.) |
649 | * ---------------- |
650 | */ |
651 | int64 |
652 | index_getbitmap(IndexScanDesc scan, TIDBitmap *bitmap) |
653 | { |
654 | int64 ntids; |
655 | |
656 | SCAN_CHECKS; |
657 | CHECK_SCAN_PROCEDURE(amgetbitmap); |
658 | |
659 | /* just make sure this is false... */ |
660 | scan->kill_prior_tuple = false; |
661 | |
662 | /* |
663 | * have the am's getbitmap proc do all the work. |
664 | */ |
665 | ntids = scan->indexRelation->rd_indam->amgetbitmap(scan, bitmap); |
666 | |
667 | pgstat_count_index_tuples(scan->indexRelation, ntids); |
668 | |
669 | return ntids; |
670 | } |
671 | |
672 | /* ---------------- |
673 | * index_bulk_delete - do mass deletion of index entries |
674 | * |
675 | * callback routine tells whether a given main-heap tuple is |
676 | * to be deleted |
677 | * |
678 | * return value is an optional palloc'd struct of statistics |
679 | * ---------------- |
680 | */ |
681 | IndexBulkDeleteResult * |
682 | index_bulk_delete(IndexVacuumInfo *info, |
683 | IndexBulkDeleteResult *stats, |
684 | IndexBulkDeleteCallback callback, |
685 | void *callback_state) |
686 | { |
687 | Relation indexRelation = info->index; |
688 | |
689 | RELATION_CHECKS; |
690 | CHECK_REL_PROCEDURE(ambulkdelete); |
691 | |
692 | return indexRelation->rd_indam->ambulkdelete(info, stats, |
693 | callback, callback_state); |
694 | } |
695 | |
696 | /* ---------------- |
697 | * index_vacuum_cleanup - do post-deletion cleanup of an index |
698 | * |
699 | * return value is an optional palloc'd struct of statistics |
700 | * ---------------- |
701 | */ |
702 | IndexBulkDeleteResult * |
703 | index_vacuum_cleanup(IndexVacuumInfo *info, |
704 | IndexBulkDeleteResult *stats) |
705 | { |
706 | Relation indexRelation = info->index; |
707 | |
708 | RELATION_CHECKS; |
709 | CHECK_REL_PROCEDURE(amvacuumcleanup); |
710 | |
711 | return indexRelation->rd_indam->amvacuumcleanup(info, stats); |
712 | } |
713 | |
714 | /* ---------------- |
715 | * index_can_return |
716 | * |
717 | * Does the index access method support index-only scans for the given |
718 | * column? |
719 | * ---------------- |
720 | */ |
721 | bool |
722 | index_can_return(Relation indexRelation, int attno) |
723 | { |
724 | RELATION_CHECKS; |
725 | |
726 | /* amcanreturn is optional; assume false if not provided by AM */ |
727 | if (indexRelation->rd_indam->amcanreturn == NULL) |
728 | return false; |
729 | |
730 | return indexRelation->rd_indam->amcanreturn(indexRelation, attno); |
731 | } |
732 | |
733 | /* ---------------- |
734 | * index_getprocid |
735 | * |
736 | * Index access methods typically require support routines that are |
737 | * not directly the implementation of any WHERE-clause query operator |
738 | * and so cannot be kept in pg_amop. Instead, such routines are kept |
739 | * in pg_amproc. These registered procedure OIDs are assigned numbers |
740 | * according to a convention established by the access method. |
741 | * The general index code doesn't know anything about the routines |
742 | * involved; it just builds an ordered list of them for |
743 | * each attribute on which an index is defined. |
744 | * |
745 | * As of Postgres 8.3, support routines within an operator family |
746 | * are further subdivided by the "left type" and "right type" of the |
747 | * query operator(s) that they support. The "default" functions for a |
748 | * particular indexed attribute are those with both types equal to |
749 | * the index opclass' opcintype (note that this is subtly different |
750 | * from the indexed attribute's own type: it may be a binary-compatible |
751 | * type instead). Only the default functions are stored in relcache |
752 | * entries --- access methods can use the syscache to look up non-default |
753 | * functions. |
754 | * |
755 | * This routine returns the requested default procedure OID for a |
756 | * particular indexed attribute. |
757 | * ---------------- |
758 | */ |
759 | RegProcedure |
760 | index_getprocid(Relation irel, |
761 | AttrNumber attnum, |
762 | uint16 procnum) |
763 | { |
764 | RegProcedure *loc; |
765 | int nproc; |
766 | int procindex; |
767 | |
768 | nproc = irel->rd_indam->amsupport; |
769 | |
770 | Assert(procnum > 0 && procnum <= (uint16) nproc); |
771 | |
772 | procindex = (nproc * (attnum - 1)) + (procnum - 1); |
773 | |
774 | loc = irel->rd_support; |
775 | |
776 | Assert(loc != NULL); |
777 | |
778 | return loc[procindex]; |
779 | } |
780 | |
781 | /* ---------------- |
782 | * index_getprocinfo |
783 | * |
784 | * This routine allows index AMs to keep fmgr lookup info for |
785 | * support procs in the relcache. As above, only the "default" |
786 | * functions for any particular indexed attribute are cached. |
787 | * |
788 | * Note: the return value points into cached data that will be lost during |
789 | * any relcache rebuild! Therefore, either use the callinfo right away, |
790 | * or save it only after having acquired some type of lock on the index rel. |
791 | * ---------------- |
792 | */ |
793 | FmgrInfo * |
794 | index_getprocinfo(Relation irel, |
795 | AttrNumber attnum, |
796 | uint16 procnum) |
797 | { |
798 | FmgrInfo *locinfo; |
799 | int nproc; |
800 | int procindex; |
801 | |
802 | nproc = irel->rd_indam->amsupport; |
803 | |
804 | Assert(procnum > 0 && procnum <= (uint16) nproc); |
805 | |
806 | procindex = (nproc * (attnum - 1)) + (procnum - 1); |
807 | |
808 | locinfo = irel->rd_supportinfo; |
809 | |
810 | Assert(locinfo != NULL); |
811 | |
812 | locinfo += procindex; |
813 | |
814 | /* Initialize the lookup info if first time through */ |
815 | if (locinfo->fn_oid == InvalidOid) |
816 | { |
817 | RegProcedure *loc = irel->rd_support; |
818 | RegProcedure procId; |
819 | |
820 | Assert(loc != NULL); |
821 | |
822 | procId = loc[procindex]; |
823 | |
824 | /* |
825 | * Complain if function was not found during IndexSupportInitialize. |
826 | * This should not happen unless the system tables contain bogus |
827 | * entries for the index opclass. (If an AM wants to allow a support |
828 | * function to be optional, it can use index_getprocid.) |
829 | */ |
830 | if (!RegProcedureIsValid(procId)) |
831 | elog(ERROR, "missing support function %d for attribute %d of index \"%s\"" , |
832 | procnum, attnum, RelationGetRelationName(irel)); |
833 | |
834 | fmgr_info_cxt(procId, locinfo, irel->rd_indexcxt); |
835 | } |
836 | |
837 | return locinfo; |
838 | } |
839 | |
840 | /* ---------------- |
841 | * index_store_float8_orderby_distances |
842 | * |
843 | * Convert AM distance function's results (that can be inexact) |
844 | * to ORDER BY types and save them into xs_orderbyvals/xs_orderbynulls |
845 | * for a possible recheck. |
846 | * ---------------- |
847 | */ |
848 | void |
849 | index_store_float8_orderby_distances(IndexScanDesc scan, Oid *orderByTypes, |
850 | IndexOrderByDistance *distances, |
851 | bool recheckOrderBy) |
852 | { |
853 | int i; |
854 | |
855 | Assert(distances || !recheckOrderBy); |
856 | |
857 | scan->xs_recheckorderby = recheckOrderBy; |
858 | |
859 | for (i = 0; i < scan->numberOfOrderBys; i++) |
860 | { |
861 | if (orderByTypes[i] == FLOAT8OID) |
862 | { |
863 | #ifndef USE_FLOAT8_BYVAL |
864 | /* must free any old value to avoid memory leakage */ |
865 | if (!scan->xs_orderbynulls[i]) |
866 | pfree(DatumGetPointer(scan->xs_orderbyvals[i])); |
867 | #endif |
868 | if (distances && !distances[i].isnull) |
869 | { |
870 | scan->xs_orderbyvals[i] = Float8GetDatum(distances[i].value); |
871 | scan->xs_orderbynulls[i] = false; |
872 | } |
873 | else |
874 | { |
875 | scan->xs_orderbyvals[i] = (Datum) 0; |
876 | scan->xs_orderbynulls[i] = true; |
877 | } |
878 | } |
879 | else if (orderByTypes[i] == FLOAT4OID) |
880 | { |
881 | /* convert distance function's result to ORDER BY type */ |
882 | #ifndef USE_FLOAT4_BYVAL |
883 | /* must free any old value to avoid memory leakage */ |
884 | if (!scan->xs_orderbynulls[i]) |
885 | pfree(DatumGetPointer(scan->xs_orderbyvals[i])); |
886 | #endif |
887 | if (distances && !distances[i].isnull) |
888 | { |
889 | scan->xs_orderbyvals[i] = Float4GetDatum((float4) distances[i].value); |
890 | scan->xs_orderbynulls[i] = false; |
891 | } |
892 | else |
893 | { |
894 | scan->xs_orderbyvals[i] = (Datum) 0; |
895 | scan->xs_orderbynulls[i] = true; |
896 | } |
897 | } |
898 | else |
899 | { |
900 | /* |
901 | * If the ordering operator's return value is anything else, we |
902 | * don't know how to convert the float8 bound calculated by the |
903 | * distance function to that. The executor won't actually need |
904 | * the order by values we return here, if there are no lossy |
905 | * results, so only insist on converting if the *recheck flag is |
906 | * set. |
907 | */ |
908 | if (scan->xs_recheckorderby) |
909 | elog(ERROR, "ORDER BY operator must return float8 or float4 if the distance function is lossy" ); |
910 | scan->xs_orderbynulls[i] = true; |
911 | } |
912 | } |
913 | } |
914 | |