| 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 | |