=== Applying patches on top of PostgreSQL commit ID 999ce9bcd80890c3d723e113bb0337c900f9f249 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Thu Sep 17 06:24:27 UTC 2026 On branch cf/7197 nothing to commit, working tree clean === using 'git am' to apply patch ./v1-0001-Detect-SSI-conflicts-when-unique-checks-reuse-keys.patch === Applying: Detect SSI conflicts when unique checks reuse keys Using index info to reconstruct a base tree... M src/backend/access/heap/heapam_handler.c M src/backend/access/heap/heapam_indexscan.c M src/backend/access/nbtree/nbtinsert.c M src/backend/access/table/tableam.c M src/include/access/heapam.h M src/include/access/tableam.h M src/test/isolation/isolation_schedule Falling back to patching base and 3-way merge... Auto-merging src/test/isolation/isolation_schedule Auto-merging src/include/access/tableam.h CONFLICT (content): Merge conflict in src/include/access/tableam.h Auto-merging src/include/access/heapam.h CONFLICT (content): Merge conflict in src/include/access/heapam.h Auto-merging src/backend/access/table/tableam.c CONFLICT (content): Merge conflict in src/backend/access/table/tableam.c Auto-merging src/backend/access/nbtree/nbtinsert.c CONFLICT (content): Merge conflict in src/backend/access/nbtree/nbtinsert.c Auto-merging src/backend/access/heap/heapam_indexscan.c CONFLICT (content): Merge conflict in src/backend/access/heap/heapam_indexscan.c Auto-merging src/backend/access/heap/heapam_handler.c CONFLICT (content): Merge conflict in src/backend/access/heap/heapam_handler.c error: Failed to merge in the changes. hint: Use 'git am --show-current-patch=diff' to see the failed patch Patch failed at 0001 Detect SSI conflicts when unique checks reuse keys When you have resolved this problem, run "git am --continue". If you prefer to skip this patch, run "git am --skip" instead. To restore the original branch and stop patching, run "git am --abort". === using patch(1) to apply patch ./v1-0001-Detect-SSI-conflicts-when-unique-checks-reuse-keys.patch === patching file src/backend/access/heap/heapam_handler.c Hunk #1 FAILED at 2677. 1 out of 1 hunk FAILED -- saving rejects to file src/backend/access/heap/heapam_handler.c.rej patching file src/backend/access/heap/heapam_indexscan.c Hunk #1 succeeded at 143 (offset 57 lines). Hunk #2 succeeded at 247 (offset 57 lines). Hunk #3 FAILED at 244. Hunk #4 succeeded at 601 with fuzz 2 (offset 289 lines). 1 out of 4 hunks FAILED -- saving rejects to file src/backend/access/heap/heapam_indexscan.c.rej patching file src/backend/access/nbtree/nbtinsert.c Hunk #4 FAILED at 565. Hunk #5 FAILED at 622. Hunk #6 succeeded at 677 (offset -2 lines). 2 out of 6 hunks FAILED -- saving rejects to file src/backend/access/nbtree/nbtinsert.c.rej patching file src/backend/access/table/tableam.c Hunk #1 FAILED at 228. Hunk #2 FAILED at 242. 2 out of 2 hunks FAILED -- saving rejects to file src/backend/access/table/tableam.c.rej patching file src/backend/storage/lmgr/predicate.c patching file src/include/access/heapam.h Hunk #1 FAILED at 440. 1 out of 1 hunk FAILED -- saving rejects to file src/include/access/heapam.h.rej patching file src/include/access/tableam.h Hunk #1 succeeded at 536 with fuzz 2 (offset 41 lines). Hunk #2 FAILED at 1336. 1 out of 2 hunks FAILED -- saving rejects to file src/include/access/tableam.h.rej patching file src/include/storage/predicate.h patching file src/test/isolation/expected/read-write-unique-5.out patching file src/test/isolation/isolation_schedule patching file src/test/isolation/specs/read-write-unique-5.spec Unstaged changes after reset: M src/backend/access/heap/heapam_indexscan.c M src/backend/access/nbtree/nbtinsert.c M src/backend/storage/lmgr/predicate.c M src/include/access/tableam.h M src/include/storage/predicate.h M src/test/isolation/isolation_schedule Removing src/backend/access/heap/heapam_handler.c.rej Removing src/backend/access/heap/heapam_indexscan.c.rej Removing src/backend/access/nbtree/nbtinsert.c.rej Removing src/backend/access/table/tableam.c.rej Removing src/include/access/heapam.h.rej Removing src/include/access/tableam.h.rej Removing src/test/isolation/expected/read-write-unique-5.out Removing src/test/isolation/specs/read-write-unique-5.spec === using 'git apply' to apply patch ./v1-0001-Detect-SSI-conflicts-when-unique-checks-reuse-keys.patch === Applied patch to 'src/backend/access/heap/heapam_handler.c' with conflicts. Applied patch to 'src/backend/access/heap/heapam_indexscan.c' with conflicts. Applied patch to 'src/backend/access/nbtree/nbtinsert.c' with conflicts. Applied patch to 'src/backend/access/table/tableam.c' with conflicts. Applied patch to 'src/backend/storage/lmgr/predicate.c' cleanly. Applied patch to 'src/include/access/heapam.h' with conflicts. Applied patch to 'src/include/access/tableam.h' with conflicts. Applied patch to 'src/include/storage/predicate.h' cleanly. Falling back to direct application... Applied patch to 'src/test/isolation/isolation_schedule' cleanly. Falling back to direct application... U src/backend/access/heap/heapam_handler.c U src/backend/access/heap/heapam_indexscan.c U src/backend/access/nbtree/nbtinsert.c U src/backend/access/table/tableam.c U src/include/access/heapam.h U src/include/access/tableam.h diff --cc src/backend/access/heap/heapam_handler.c index 6adb760b54f,2f4db724ae6..00000000000 --- a/src/backend/access/heap/heapam_handler.c +++ b/src/backend/access/heap/heapam_handler.c @@@ -2666,9 -2673,11 +2666,17 @@@ static const TableAmRoutine heapam_meth .parallelscan_initialize = table_block_parallelscan_initialize, .parallelscan_reinitialize = table_block_parallelscan_reinitialize, ++<<<<<<< ours + .index_scan_begin = heapam_index_scan_begin, + .index_scan_reset = heapam_index_scan_reset, + .index_scan_end = heapam_index_scan_end, ++======= + .index_fetch_begin = heapam_index_fetch_begin, + .index_fetch_reset = heapam_index_fetch_reset, + .index_fetch_end = heapam_index_fetch_end, + .index_fetch_tuple = heapam_index_fetch_tuple, + .index_fetch_tuple_check = heapam_index_fetch_tuple_check, ++>>>>>>> theirs .tuple_insert = heapam_tuple_insert, .tuple_insert_speculative = heapam_tuple_insert_speculative, diff --cc src/backend/access/heap/heapam_indexscan.c index 0ae028ecd41,1d5ed438cf3..00000000000 --- a/src/backend/access/heap/heapam_indexscan.c +++ b/src/backend/access/heap/heapam_indexscan.c @@@ -285,173 -244,28 +301,192 @@@ heap_hot_search_buffer_internal(ItemPoi return false; } ++<<<<<<< ours +/* xs_getnext_slot callback: amgettuple, plain index scan */ +static bool +heapam_index_plain_tuple_getnext_slot(IndexScanDesc scan, + ScanDirection direction, + TupleTableSlot *slot) +{ + Assert(!scan->xs_want_itup); + Assert(scan->indexRelation->rd_indam->amgettuple != NULL); + + return heapam_index_getnext_slot(scan, direction, slot, false); +} + +/* xs_getnext_slot callback: amgettuple, index-only scan */ +static bool +heapam_index_only_tuple_getnext_slot(IndexScanDesc scan, + ScanDirection direction, + TupleTableSlot *slot) +{ + Assert(scan->xs_want_itup); + Assert(scan->indexRelation->rd_indam->amgettuple != NULL); + + return heapam_index_getnext_slot(scan, direction, slot, true); +} + +/* + * Common implementation for both heapam_index_*_getnext_slot variants. + * + * The result is true if a tuple satisfying the scan keys and the snapshot was + * found, false otherwise. This is per the table_index_getnext_slot + * interface. + * + * The index_only parameter is a compile-time constant at each call site, + * allowing the compiler to specialize the code for each variant. + */ +static pg_always_inline bool +heapam_index_getnext_slot(IndexScanDesc scan, ScanDirection direction, + TupleTableSlot *slot, bool index_only) +{ + Assert(TransactionIdIsValid(RecentXmin)); + Assert(index_only || scan->xs_visited_pages_limit == 0); + + for (;;) + { + IndexScanHeapData *hscan; + bool all_visible; + + /* + * Get the next TID from the index, unless we're still working through + * a HOT chain (index-only scans never do that, and plain index scans + * only do it with a non-MVCC snapshot) + */ + Assert(!index_only || !scan->xs_heap_continue); + if (index_only || likely(!scan->xs_heap_continue)) + { + if (!tableam_index_getnext_tid(scan, direction)) + return false; + } + + /* The scan's next TID was set in scan->xs_heaptid for us */ + Assert(ItemPointerIsValid(&scan->xs_heaptid)); + + hscan = (IndexScanHeapData *) scan->xs_table_opaque; + + if (!index_only) + { + /* Plain index scan */ + if (!heapam_index_heap_fetch(scan, hscan, slot, false)) + continue; /* no visible tuple, try next index entry */ + } + else + { + /* + * Note: VM_ALL_VISIBLE does not lock the visibility map buffer, + * so the result could be slightly stale. See the comments above + * visibilitymap_get_status for why this is okay. + */ + all_visible = VM_ALL_VISIBLE(scan->heapRelation, + ItemPointerGetBlockNumber(&scan->xs_heaptid), + &hscan->xs_vmbuffer); + + /* Page isn't all-visible, so verify visibility with a heap fetch */ + if (unlikely(!all_visible)) + { + if (!heapam_index_only_heap_fetch(scan)) + { + /* No visible tuple */ + if (heapam_index_visited_pages_exceeded(scan)) + return false; /* give up */ + + continue; /* try next index entry */ + } + } + else + { + /* + * Index-only scan with all-visible item. + * + * We won't access the heap, so we'll need to take a predicate + * lock explicitly, as if we had. For now we do that at page + * level. + */ + PredicateLockPage(scan->heapRelation, + ItemPointerGetBlockNumber(&scan->xs_heaptid), + scan->xs_snapshot); + } + + /* + * Fill slot with data returned by the index AM (during plain + * scans heapam_index_heap_fetch does this for us instead) + */ + tableam_index_fill_ios_slot(scan, slot); + } + + return true; + } + + pg_unreachable(); + + return false; +} + +/* + * Get the scan's next heap tuple. + * + * Returns true if a visible heap tuple associated with the index TID most + * recently fetched by our caller in scan->xs_heaptid was found, false if no + * more matching tuples exist. (There can be more than one matching tuple + * because of HOT chains, although when using an MVCC snapshot it should be + * impossible for more than one such tuple to exist.) + * + * Plain index scans have us store the tuple in their slot, and its buffer + * stays pinned until a later call here (or heapam_index_scan_end) releases + * it. Index-only scans just need us to verify tuple visibility, so they pass + * a NULL slot. + * + * When the TID's whole HOT chain turns out to be dead, we arrange for the + * index AM to kill its entry for the TID before returning false. + */ +static pg_always_inline bool +heapam_index_heap_fetch(IndexScanDesc scan, IndexScanHeapData *hscan, + TupleTableSlot *slot, bool index_only) ++======= + bool + heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, + Snapshot snapshot, HeapTuple heapTuple, + bool *all_dead, bool first_call) + { + return heap_hot_search_buffer_internal(tid, relation, buffer, snapshot, + NULL, heapTuple, all_dead, first_call, + NULL); + } + + bool + heapam_index_fetch_tuple(struct IndexFetchTableData *scan, + ItemPointer tid, + Snapshot snapshot, + TupleTableSlot *slot, + bool *heap_continue, bool *all_dead) ++>>>>>>> theirs { - IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan; - BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot; + Relation rel = scan->heapRelation; + ItemPointer tid = &scan->xs_heaptid; + Snapshot snapshot = scan->xs_snapshot; + HeapTupleData tupdata; + HeapTuple heapTuple; bool got_heap_tuple; + bool all_dead; - Assert(TTS_IS_BUFFERTUPLE(slot)); + if (!index_only) + { + /* Plain index scans have us store fetched tuple in their slot */ + BufferHeapTupleTableSlot *bslot = (BufferHeapTupleTableSlot *) slot; + + Assert(TTS_IS_BUFFERTUPLE(slot)); + heapTuple = &bslot->base.tupdata; + } + else + { + /* Index-only scans only need to verify tuple visibility */ + pg_assume(slot == NULL); + heapTuple = &tupdata; + + if (scan->instrument) + scan->instrument->ntabletuplefetches++; + } /* We can skip the buffer-switching logic if we're on the same page. */ if (hscan->xs_blk != ItemPointerGetBlockNumber(tid)) @@@ -531,57 -323,35 +566,91 @@@ return got_heap_tuple; } ++<<<<<<< ours +/* + * Out-of-line heapam_index_heap_fetch wrapper for index-only scans. + * + * Index-only scans usually avoid heap fetches using the visibility map, so + * keeping their fetch out of line keeps the frame of their getnext_slot + * callback small. + */ +static pg_noinline bool +heapam_index_only_heap_fetch(IndexScanDesc scan) +{ + IndexScanHeapData *hscan = (IndexScanHeapData *) scan->xs_table_opaque; + + return heapam_index_heap_fetch(scan, hscan, NULL, true); +} + +/* + * Called when we scanned a whole HOT chain and found only dead tuples: + * arrange for the index AM to kill its entry for that TID. We do not do this + * when in recovery because it may violate MVCC to do so. See comments in + * RelationGetIndexScan(). + */ +static pg_noinline void +heapam_index_kill_item(IndexScanDesc scan) +{ + if (scan->xactStartedInRecovery) + return; + + /* + * Tell amgettuple-based index AM to kill its entry for that TID. The + * next tableam_index_getnext_tid call will pass that along to the index + * AM, before unsetting the flag again. + */ + scan->kill_prior_tuple = true; +} + +/* + * Did an index-only scan switch heap pages more times than the caller's + * visited-pages limit allows? + * + * Caller passes scan rather than hscan to avoiding keeping hscan live across + * heap fetches. + */ +static inline bool +heapam_index_visited_pages_exceeded(IndexScanDesc scan) +{ + IndexScanHeapData *hscan; + + if (likely(scan->xs_visited_pages_limit == 0)) + return false; + + hscan = (IndexScanHeapData *) scan->xs_table_opaque; + + return hscan->xs_blkswitch_count > scan->xs_visited_pages_limit; ++======= + bool + heapam_index_fetch_tuple_check(struct IndexFetchTableData *scan, + ItemPointer tid, Snapshot snapshot, + Snapshot crosscheck, bool *all_dead, + TransactionId *conflict_xid) + { + IndexFetchHeapData *hscan = (IndexFetchHeapData *) scan; + HeapTupleData heap_tuple; + bool found; + + if (hscan->xs_blk != ItemPointerGetBlockNumber(tid)) + { + hscan->xs_blk = ItemPointerGetBlockNumber(tid); + + if (BufferIsValid(hscan->xs_cbuf)) + ReleaseBuffer(hscan->xs_cbuf); + + hscan->xs_cbuf = ReadBuffer(hscan->xs_base.rel, hscan->xs_blk); + heap_page_prune_opt(hscan->xs_base.rel, hscan->xs_cbuf, + &hscan->xs_vmbuffer, + hscan->xs_base.flags & SO_HINT_REL_READ_ONLY); + } + + LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_SHARE); + found = heap_hot_search_buffer_internal(tid, hscan->xs_base.rel, + hscan->xs_cbuf, snapshot, crosscheck, + &heap_tuple, all_dead, true, + conflict_xid); + LockBuffer(hscan->xs_cbuf, BUFFER_LOCK_UNLOCK); + + return found; ++>>>>>>> theirs } diff --cc src/backend/access/nbtree/nbtinsert.c index d5d964301c1,7cce49d951f..00000000000 --- a/src/backend/access/nbtree/nbtinsert.c +++ b/src/backend/access/nbtree/nbtinsert.c @@@ -560,8 -563,12 +563,17 @@@ _bt_check_unique(Relation rel, BTInsert * with optimizations like heap's HOT, we have just a single * index entry for the entire chain. */ ++<<<<<<< ours + else if (table_fetch_tid(heapRel, &htid, &SnapshotDirty, + &all_dead)) ++======= + else if (table_index_fetch_tuple_check(heapRel, &htid, + &SnapshotDirty, + IsolationIsSerializable() && + ActiveSnapshotSet() ? + GetActiveSnapshot() : NULL, + &all_dead, &conflict_xid)) ++>>>>>>> theirs { TransactionId xwait; @@@ -617,7 -624,9 +629,13 @@@ * entry. */ htid = itup->t_tid; ++<<<<<<< ours + if (table_fetch_tid(heapRel, &htid, SnapshotSelf, NULL)) ++======= + if (table_index_fetch_tuple_check(heapRel, &htid, + SnapshotSelf, NULL, NULL, + &conflict_xid)) ++>>>>>>> theirs { /* Normal case --- it's still live */ } diff --cc src/backend/access/table/tableam.c index 7aaa8d87150,931c7348fb5..00000000000 --- a/src/backend/access/table/tableam.c +++ b/src/backend/access/table/tableam.c @@@ -222,6 -222,42 +222,45 @@@ table_beginscan_parallel_tidrange(Relat } ++<<<<<<< ours ++======= + /* ---------------------------------------------------------------------------- + * Index scan related functions. + * ---------------------------------------------------------------------------- + */ + + /* + * To perform that check simply start an index scan, do the table AM lookup, + * and shut everything down again. If there frequently are live index + * pointers also matching a unique index key, the CPU overhead of this routine + * is unlikely to matter. + * + * Note that *tid may be modified when we return true if the AM supports + * storing multiple row versions reachable via a single index entry (like + * heap's HOT). + */ + bool + table_index_fetch_tuple_check(Relation rel, + ItemPointer tid, + Snapshot snapshot, + Snapshot crosscheck, + bool *all_dead, + TransactionId *conflict_xid) + { + IndexFetchTableData *scan; + bool found; + + scan = table_index_fetch_begin(rel, SO_NONE); + *conflict_xid = InvalidTransactionId; + found = rel->rd_tableam->index_fetch_tuple_check(scan, tid, snapshot, + crosscheck, all_dead, conflict_xid); + table_index_fetch_end(scan); + + return found; + } + + ++>>>>>>> theirs /* ------------------------------------------------------------------------ * Functions for non-modifying operations on individual tuples * ------------------------------------------------------------------------ diff --cc src/include/access/heapam.h index 9e35961fb9e,dc02116face..00000000000 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@@ -439,6 -436,14 +439,17 @@@ extern void heapam_index_scan_end(Index extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer, Snapshot snapshot, HeapTuple heapTuple, bool *all_dead, bool first_call); ++<<<<<<< ours ++======= + extern bool heapam_index_fetch_tuple(struct IndexFetchTableData *scan, + ItemPointer tid, Snapshot snapshot, + TupleTableSlot *slot, bool *heap_continue, + bool *all_dead); + extern bool heapam_index_fetch_tuple_check(struct IndexFetchTableData *scan, + ItemPointer tid, Snapshot snapshot, + Snapshot crosscheck, bool *all_dead, + TransactionId *conflict_xid); ++>>>>>>> theirs /* in heap/pruneheap.c */ extern void heap_page_prune_opt(Relation relation, Buffer buffer, diff --cc src/include/access/tableam.h index ea3f2a6be99,074535d985f..00000000000 --- a/src/include/access/tableam.h +++ b/src/include/access/tableam.h @@@ -454,38 -454,61 +454,83 @@@ typedef struct TableAmRoutin * flags is a bitmask of ScanOptions affecting underlying table scan * behavior. See scan_begin() for more information on passing these. * - * Tuples for an index scan can then be fetched via index_fetch_tuple. + * Callback is responsible for setting scan->xs_getnext_slot, the callback + * that table_index_getnext_slot() dispatches to. Tuples are then + * returned through the caller's slot. No separate xs_getnext_slot + * callback exists in this struct. + * + * In principle a single general-purpose callback (stored here) would + * suffice, but using specialized variants allows the table AM to provide + * minimal code based on conditions that are fixed for the whole scan as + * an optimization (e.g., variants for plain index scans and index-only + * scans, each with fewer branches). + * + * Plain index scans use whatever slot type the table AM's slot_callbacks + * chooses. Index-only scans always use a virtual slot, which is filled + * using index data in a standardized way (though determining which index + * tuples satisfy scan->xs_snapshot is still up to the table AM). + * + * The xs_getnext_slot callback is also responsible for whatever + * bookkeeping its callers expect of an index scan, such as maintaining + * instrumentation counters. */ - struct IndexFetchTableData *(*index_fetch_begin) (Relation rel, uint32 flags); + void (*index_scan_begin) (IndexScanDesc scan, uint32 flags); /* - * Reset index fetch. Typically this will release cross index fetch - * resources held in IndexFetchTableData. + * Inform the table AM that there's to be either a rescan or a restore of + * a marked position. */ - void (*index_fetch_reset) (struct IndexFetchTableData *data); + void (*index_scan_reset) (IndexScanDesc scan); /* - * Release resources and deallocate index fetch. + * Release resources and deallocate index scan state. */ ++<<<<<<< ours + void (*index_scan_end) (IndexScanDesc scan); ++======= + void (*index_fetch_end) (struct IndexFetchTableData *data); + + /* + * Fetch tuple at `tid` into `slot`, after doing a visibility test + * according to `snapshot`. If a tuple was found and passed the visibility + * test, return true, false otherwise. + * + * Note that AMs that do not necessarily update indexes when indexed + * columns do not change, need to return the current/correct version of + * the tuple that is visible to the snapshot, even if the tid points to an + * older version of the tuple. + * + * *call_again is false on the first call to index_fetch_tuple for a tid. + * If there potentially is another tuple matching the tid, *call_again + * needs to be set to true by index_fetch_tuple, signaling to the caller + * that index_fetch_tuple should be called again for the same tid. + * + * *all_dead, if all_dead is not NULL, should be set to true by + * index_fetch_tuple iff it is guaranteed that no backend needs to see + * that tuple. Index AMs can use that to avoid returning that tid in + * future searches. + */ + bool (*index_fetch_tuple) (struct IndexFetchTableData *scan, + ItemPointer tid, + Snapshot snapshot, + TupleTableSlot *slot, + bool *call_again, bool *all_dead); + + /* + * Check whether an index TID points to a tuple visible to `snapshot`, as + * needed for unique checks. If no tuple is visible to `snapshot`, but a + * tuple deleted by another transaction is visible to `crosscheck`, return + * the deleting transaction's XID in *conflict_xid. `crosscheck` may be + * NULL when this information is not needed. + */ + bool (*index_fetch_tuple_check) (struct IndexFetchTableData *scan, + ItemPointer tid, + Snapshot snapshot, + Snapshot crosscheck, + bool *all_dead, + TransactionId *conflict_xid); + ++>>>>>>> theirs /* ------------------------------------------------------------------------ * Callbacks for non-modifying operations on individual tuples @@@ -1283,32 -1292,53 +1328,47 @@@ table_index_scan_end(IndexScanDesc scan } /* - * Fetches, as part of an index scan, tuple at `tid` into `slot`, after doing - * a visibility test according to `snapshot`. If a tuple was found and passed - * the visibility test, returns true, false otherwise. Note that *tid may be - * modified when we return true (see later remarks on multiple row versions - * reachable via a single index entry). + * Return the next tuple from an index scan through `slot`, scanning in the + * specified direction. Returns true if a tuple satisfying the scan keys and + * the snapshot was found, false otherwise. * - * *call_again needs to be false on the first call to table_index_fetch_tuple() for - * a tid. If there potentially is another tuple matching the tid, *call_again - * will be set to true, signaling that table_index_fetch_tuple() should be called - * again for the same tid. + * Dispatches through scan->xs_getnext_slot, which is resolved once by the + * table AM's index_scan_begin callback. * - * *all_dead, if all_dead is not NULL, will be set to true by - * table_index_fetch_tuple() iff it is guaranteed that no backend needs to see - * that tuple. Index AMs can use that to avoid returning that tid in future - * searches. + * On success, resources (like buffer pins) are likely to be held, and will be + * released by a future table_index_getnext_slot or table_index_scan_end call. * - * The difference between this function and table_tuple_fetch_row_version() - * is that this function returns the currently visible version of a row if - * the AM supports storing multiple row versions reachable via a single index - * entry (like heap's HOT). Whereas table_tuple_fetch_row_version() only - * evaluates the tuple exactly at `tid`. Outside of index entry ->table tuple - * lookups, table_tuple_fetch_row_version() is what's usually needed. + * Note: caller must check scan->xs_recheck, and perform rechecking of the + * scan keys if required. We do not do that here because we don't have + * enough information to do it efficiently in the general case. Similarly, + * for ordered scans, the caller must check scan->xs_recheckorderby and + * recheck the ORDER BY expressions for itself. */ static inline bool -table_index_fetch_tuple(struct IndexFetchTableData *scan, - ItemPointer tid, - Snapshot snapshot, - TupleTableSlot *slot, - bool *call_again, bool *all_dead) +table_index_getnext_slot(IndexScanDesc scan, ScanDirection direction, + TupleTableSlot *slot) { - return scan->rel->rd_tableam->index_fetch_tuple(scan, tid, snapshot, - slot, call_again, - all_dead); -} + /* See index_scan_begin for an explanation of index-only scan slot type */ + Assert(!scan->xs_want_itup || TTS_IS_VIRTUAL(slot)); + Assert(scan->xs_table_opaque); ++<<<<<<< ours + return scan->xs_getnext_slot(scan, direction, slot); +} ++======= + /* + * This is a convenience wrapper around table_index_fetch_tuple() which + * returns whether there are table tuple items corresponding to an index + * entry. This likely is only useful to verify if there's a conflict in a + * unique index. + */ + extern bool table_index_fetch_tuple_check(Relation rel, + ItemPointer tid, + Snapshot snapshot, + Snapshot crosscheck, + bool *all_dead, + TransactionId *conflict_xid); ++>>>>>>> theirs /* ------------------------------------------------------------------------