=== Applying patches on top of PostgreSQL commit ID 6168c65ddcac4494fb292418cc774897791e0667 === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Mon Sep 7 10:46:27 UTC 2026 On branch cf/5721 nothing to commit, working tree clean === using 'git am' to apply patch ./v3-0001-Expose-visibility-checking-shim-for-index-usage.patch === Applying: Expose visibility checking shim for index usage Using index info to reconstruct a base tree... M src/backend/executor/nodeIndexonlyscan.c M src/backend/utils/adt/selfuncs.c M src/include/access/relscan.h M src/include/access/tableam.h Falling back to patching base and 3-way merge... Auto-merging src/include/access/tableam.h Auto-merging src/include/access/relscan.h Auto-merging src/backend/utils/adt/selfuncs.c Auto-merging src/backend/executor/nodeIndexonlyscan.c CONFLICT (content): Merge conflict in src/backend/executor/nodeIndexonlyscan.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 Expose visibility checking shim for index usage 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 ./v3-0001-Expose-visibility-checking-shim-for-index-usage.patch === patching file src/backend/access/index/indexam.c patching file src/backend/executor/nodeIndexonlyscan.c Hunk #2 FAILED at 164. 1 out of 2 hunks FAILED -- saving rejects to file src/backend/executor/nodeIndexonlyscan.c.rej patching file src/backend/utils/adt/selfuncs.c Hunk #1 succeeded at 7298 (offset 1 line). Hunk #2 succeeded at 7395 (offset 1 line). patching file src/include/access/relscan.h Hunk #1 succeeded at 186 (offset -2 lines). patching file src/include/access/tableam.h Unstaged changes after reset: M src/backend/access/index/indexam.c M src/backend/executor/nodeIndexonlyscan.c M src/backend/utils/adt/selfuncs.c M src/include/access/relscan.h M src/include/access/tableam.h Removing src/backend/executor/nodeIndexonlyscan.c.rej === using 'git apply' to apply patch ./v3-0001-Expose-visibility-checking-shim-for-index-usage.patch === Applied patch to 'src/backend/access/index/indexam.c' cleanly. Applied patch to 'src/backend/executor/nodeIndexonlyscan.c' with conflicts. Applied patch to 'src/backend/utils/adt/selfuncs.c' cleanly. Applied patch to 'src/include/access/relscan.h' cleanly. Applied patch to 'src/include/access/tableam.h' cleanly. U src/backend/executor/nodeIndexonlyscan.c diff --cc src/backend/executor/nodeIndexonlyscan.c index 856df2ba51d,46e8c8d806b..00000000000 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@@ -136,41 -139,87 +139,94 @@@ IndexOnlyNext(IndexOnlyScanState *node * Note on Memory Ordering Effects: visibilitymap_get_status does not * lock the visibility map buffer, and therefore the result we read * here could be slightly stale. However, it can't be stale enough to ++<<<<<<< ours + * matter; see comments above visibilitymap_get_status for the full + * argument. It's worth going through this complexity to avoid + * needing to lock the VM buffer, which could cause significant + * contention. ++======= + * matter. + * + * We need to detect clearing a VM bit due to an insert right away, + * because the tuple is present in the index page but not visible. The + * reading of the TID by this scan (using a shared lock on the index + * buffer) is serialized with the insert of the TID into the index + * (using an exclusive lock on the index buffer). Because the VM bit + * is cleared before updating the index, and locking/unlocking of the + * index page acts as a full memory barrier, we are sure to see the + * cleared bit if we see a recently-inserted TID. + * + * Deletes do not update the index page (only VACUUM will clear out + * the TID), so the clearing of the VM bit by a delete is not + * serialized with this test below, and we may see a value that is + * significantly stale. However, we don't care about the delete right + * away, because the tuple is still visible until the deleting + * transaction commits or the statement ends (if it's our + * transaction). In either case, the lock on the VM buffer will have + * been released (acting as a write barrier) after clearing the bit. + * And for us to have a snapshot that includes the deleting + * transaction (making the tuple invisible), we must have acquired + * ProcArrayLock after that time, acting as a read barrier. + * + * It's worth going through this complexity to avoid needing to lock + * the VM buffer, which could cause significant contention. + * + * The index doing these checks for us doesn't materially change these + * considerations. ++>>>>>>> theirs */ - if (!VM_ALL_VISIBLE(scandesc->heapRelation, - ItemPointerGetBlockNumber(tid), - &node->ioss_VMBuffer)) + if (vischeck == TMVC_Unchecked) + vischeck = table_index_vischeck_tuple(scandesc->heapRelation, + &node->ioss_VMBuffer, + tid); + + Assert(vischeck != TMVC_Unchecked); + + switch (vischeck) { - /* - * Rats, we have to visit the heap to check visibility. - */ - InstrCountTuples2(node, 1); - if (!index_fetch_heap(scandesc, node->ioss_TableSlot)) - continue; /* no visible tuple, try next index entry */ - - ExecClearTuple(node->ioss_TableSlot); - - /* - * Only MVCC snapshots are supported here, so there should be no - * need to keep following the HOT chain once a visible entry has - * been found. If we did want to allow that, we'd need to keep - * more state to remember not to call index_getnext_tid next time. - */ - if (scandesc->xs_heap_continue) - elog(ERROR, "non-MVCC snapshots are not supported in index-only scans"); - - /* - * Note: at this point we are holding a pin on the heap page, as - * recorded in scandesc->xs_cbuf. We could release that pin now, - * but it's not clear whether it's a win to do so. The next index - * entry might require a visit to the same heap page. - */ - - tuple_from_heap = true; + case TMVC_Unchecked: + elog(ERROR, "Failed to check visibility for tuple"); + + /* + * In case of compilers that don't undertand that elog(ERROR) + * doens't exit, but which do have a functional + * -Wimplicit-fallthrough warning: + */ + pg_fallthrough; + case TMVC_MaybeVisible: + { + /* + * Rats, we have to visit the heap to check visibility. + */ + InstrCountTuples2(node, 1); + if (!index_fetch_heap(scandesc, node->ioss_TableSlot)) + continue; /* no visible tuple, try next index entry */ + + ExecClearTuple(node->ioss_TableSlot); + + /* + * Only MVCC snapshots are supported here, so there should + * be no need to keep following the HOT chain once a + * visible entry has been found. If we did want to allow + * that, we'd need to keep more state to remember not to + * call index_getnext_tid next time. + */ + if (scandesc->xs_heap_continue) + elog(ERROR, "non-MVCC snapshots are not supported in index-only scans"); + + /* + * Note: at this point we are holding a pin on the heap + * page, as recorded in scandesc->xs_cbuf. We could + * release that pin now, but it's not clear whether it's a + * win to do so. The next index entry might require a + * visit to the same heap page. + */ + + tuple_from_heap = true; + break; + } + case TMVC_Visible: + break; } /* Fill the scan tuple slot with data from the index */