=== Applying patches on top of PostgreSQL commit ID a27893df45ec5d8c657899202e9cf0b9a816fe2f === /etc/rc.d/jail: WARNING: Per-jail configuration via jail_* variables is obsolete. Please consider migrating to /etc/jail.conf. Tue Jul 8 13:12:18 UTC 2025 On branch cf/5542 nothing to commit, working tree clean === using 'git am' to apply patch ./v12-0001-IOS-TableAM-Support-AM-specific-fast-visibility-.patch === Applying: IOS/TableAM: Support AM-specific fast visibility tests === using 'git am' to apply patch ./v12-0002-GIST-Fix-visibility-issues-in-IOS.patch === Applying: GIST: Fix visibility issues in IOS === using 'git am' to apply patch ./v12-0003-SP-GIST-Fix-visibility-issues-in-IOS.patch === Applying: SP-GIST: Fix visibility issues in IOS === using 'git am' to apply patch ./v12-0004-NBTree-Reduce-Index-Only-Scan-pin-duration.patch === Applying: NBTree: Reduce Index-Only Scan pin duration Using index info to reconstruct a base tree... M src/backend/access/nbtree/nbtree.c M src/backend/access/nbtree/nbtsearch.c M src/include/access/nbtree.h Falling back to patching base and 3-way merge... Auto-merging src/include/access/nbtree.h Auto-merging src/backend/access/nbtree/nbtsearch.c CONFLICT (content): Merge conflict in src/backend/access/nbtree/nbtsearch.c Auto-merging src/backend/access/nbtree/nbtree.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 NBTree: Reduce Index-Only Scan pin duration 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". Unstaged changes after reset: M src/backend/access/nbtree/nbtree.c M src/backend/access/nbtree/nbtsearch.c M src/include/access/nbtree.h === using patch(1) to apply patch ./v12-0004-NBTree-Reduce-Index-Only-Scan-pin-duration.patch === patching file src/backend/access/nbtree/nbtree.c Hunk #1 succeeded at 364 (offset 4 lines). Hunk #2 succeeded at 436 (offset 32 lines). Hunk #3 succeeded at 493 (offset 32 lines). patching file src/backend/access/nbtree/nbtsearch.c Hunk #1 FAILED at 25. Hunk #3 FAILED at 70. Hunk #4 succeeded at 2065 (offset 58 lines). Hunk #5 succeeded at 2097 (offset 58 lines). Hunk #6 succeeded at 2135 (offset 58 lines). Hunk #7 succeeded at 2161 (offset 58 lines). Hunk #8 FAILED at 2269. Hunk #9 FAILED at 2426. 4 out of 9 hunks FAILED -- saving rejects to file src/backend/access/nbtree/nbtsearch.c.rej patching file src/include/access/nbtree.h Hunk #2 succeeded at 1073 with fuzz 2 (offset 1 line). Unstaged changes after reset: M src/backend/access/nbtree/nbtree.c M src/backend/access/nbtree/nbtsearch.c M src/include/access/nbtree.h Removing src/backend/access/nbtree/nbtsearch.c.rej === using 'git apply' to apply patch ./v12-0004-NBTree-Reduce-Index-Only-Scan-pin-duration.patch === Applied patch to 'src/backend/access/nbtree/nbtree.c' cleanly. Applied patch to 'src/backend/access/nbtree/nbtsearch.c' with conflicts. Applied patch to 'src/include/access/nbtree.h' cleanly. U src/backend/access/nbtree/nbtsearch.c diff --cc src/backend/access/nbtree/nbtsearch.c index 4af1ff1e9e5,ed173bf7246..00000000000 --- a/src/backend/access/nbtree/nbtsearch.c +++ b/src/backend/access/nbtree/nbtsearch.c @@@ -25,7 -25,7 +25,11 @@@ #include "utils/rel.h" ++<<<<<<< ours +static inline void _bt_drop_lock_and_maybe_pin(Relation rel, BTScanOpaque so); ++======= + static void _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp, BTScanOpaque so); ++>>>>>>> theirs static Buffer _bt_moveright(Relation rel, Relation heaprel, BTScanInsert key, Buffer buf, bool forupdate, BTStack stack, int access); @@@ -57,29 -63,120 +67,136 @@@ static bool _bt_endpoint(IndexScanDesc /* * _bt_drop_lock_and_maybe_pin() * - * Unlock the buffer; and if it is safe to release the pin, do that, too. - * This will prevent vacuum from stalling in a blocked state trying to read a - * page when a cursor is sitting on it. - * - * See nbtree/README section on making concurrent TID recycling safe. + * Unlock so->currPos.buf. If scan is so->dropPin, drop the pin, too. + * Dropping the pin prevents VACUUM from blocking on acquiring a cleanup lock. */ ++<<<<<<< ours +static inline void +_bt_drop_lock_and_maybe_pin(Relation rel, BTScanOpaque so) +{ + if (!so->dropPin) ++======= + static void + _bt_drop_lock_and_maybe_pin(IndexScanDesc scan, BTScanPos sp, BTScanOpaque so) + { + _bt_unlockbuf(scan->indexRelation, sp->buf); + + /* + * Do some visibility checks if this is an index-only scan; allowing us to + * drop the pin on this page before we have returned all tuples from this + * IOS to the executor. + */ + if (scan->xs_want_itup && DEBUG_IOS_VISCHECKS_ENABLED) + { + int initOffset = sp->firstItem; + int ntids = 1 + sp->lastItem - initOffset; + + if (ntids > 0) + { + TM_IndexVisibilityCheckOp visCheck; + Relation heaprel = scan->heapRelation; + TM_VisCheck *check; + BTScanPosItem *item; + + visCheck.checkntids = ntids; + + if (so->vischeckcap == 0) + { + so->vischecksbuf = palloc_array(TM_VisCheck, ntids); + so->vischeckcap = ntids; + } + else if (so->vischeckcap < visCheck.checkntids) + { + so->vischecksbuf = repalloc_array(so->vischecksbuf, + TM_VisCheck, ntids); + so->vischeckcap = ntids; + } + + visCheck.checktids = so->vischecksbuf; + visCheck.vmbuf = &so->vmbuf; + + check = so->vischecksbuf; + item = &so->currPos.items[initOffset]; + + for (int i = 0; i < visCheck.checkntids; i++) + { + Assert(item->visrecheck == TMVC_Unchecked); + Assert(ItemPointerIsValid(&item->heapTid)); + + PopulateTMVischeck(check, &item->heapTid, initOffset + i); + + item++; + check++; + } + + table_index_vischeck_tuples(heaprel, &visCheck); + check = so->vischecksbuf; + + for (int i = 0; i < visCheck.checkntids; i++) + { + item = &so->currPos.items[check->idxoffnum]; + /* We must have a valid visibility check result */ + Assert(check->vischeckresult != TMVC_Unchecked); + /* The offset number should still indicate the right item */ + Assert(check->tidblkno == ItemPointerGetBlockNumberNoCheck(&item->heapTid)); + Assert(check->tidoffset == ItemPointerGetOffsetNumberNoCheck(&item->heapTid)); + + /* Store the visibility check result */ + item->visrecheck = check->vischeckresult; + check++; + } + } + } + + /* + * We may need to hold a pin on the page for one of several reasons: + * + * 1.) To safely apply kill_prior_tuple, we need to know that the tuples + * were not removed from the page (and subsequently re-inserted). + * A page's LSN can also allow us to detect modifications on the page, + * which then allows us to bail out of setting the hint bits, but that + * requires the index to be WAL-logged; so unless the index is WAL-logged + * we must hold a pin on the page to apply the kill_prior_tuple + * optimization. + * + * 2.) Non-MVCC scans need pin coupling to make sure the scan covers + * exactly the whole index keyspace. + * + * 3.) For Index-Only Scans, the scan needs to check the visibility of the + * table tuple while the relevant index tuple is guaranteed to still be + * contained in the index (so that vacuum hasn't yet marked any pages that + * could contain the value as ALL_VISIBLE after reclaiming a dead tuple + * that might be buffered in the scan). A pin must therefore be held + * at least while the basic visibility of the page's tuples is being + * checked. + * + * For cases 1 and 2, we must hold the pin after we've finished processing + * the index page. + * + * For case 3, we can release the pin if we first do the visibility checks + * of to-be-returned tuples using table_index_vischeck_tuples, which we've + * done just above. + */ + if (IsMVCCSnapshot(scan->xs_snapshot) && + RelationNeedsWAL(scan->indexRelation) && + (!scan->xs_want_itup || DEBUG_IOS_VISCHECKS_ENABLED)) ++>>>>>>> theirs { - ReleaseBuffer(sp->buf); - sp->buf = InvalidBuffer; + /* Just drop the lock (not the pin) */ + _bt_unlockbuf(rel, so->currPos.buf); + return; } + + /* + * Drop both the lock and the pin. + * + * Have to set so->currPos.lsn so that _bt_killitems has a way to detect + * when concurrent heap TID recycling by VACUUM might have taken place. + */ + Assert(RelationNeedsWAL(rel)); + so->currPos.lsn = BufferGetLSNAtomic(so->currPos.buf); + _bt_relbuf(rel, so->currPos.buf); + so->currPos.buf = InvalidBuffer; } /* @@@ -2309,7 -2365,7 +2439,11 @@@ _bt_readfirstpage(IndexScanDesc scan, O * so->currPos.buf in preparation for btgettuple returning tuples. */ Assert(BTScanPosIsPinned(so->currPos)); ++<<<<<<< ours + _bt_drop_lock_and_maybe_pin(rel, so); ++======= + _bt_drop_lock_and_maybe_pin(scan, &so->currPos, so); ++>>>>>>> theirs return true; } @@@ -2469,7 -2522,7 +2603,11 @@@ _bt_readnextpage(IndexScanDesc scan, Bl */ Assert(so->currPos.currPage == blkno); Assert(BTScanPosIsPinned(so->currPos)); ++<<<<<<< ours + _bt_drop_lock_and_maybe_pin(rel, so); ++======= + _bt_drop_lock_and_maybe_pin(scan, &so->currPos, so); ++>>>>>>> theirs return true; }