From 76a6c5da407e461b337ff48dbdbefd144f392187 Mon Sep 17 00:00:00 2001 From: Peter Geoghegan Date: Thu, 8 Oct 2026 14:21:09 -0400 Subject: [PATCH] Remove faulty nbtree page deletion assertion. _bt_pagedel only builds a search stack when it needs one to locate the downlink of the page that it's about to mark half-dead (and when it doesn't already have one from an earlier sibling deletion that took place during the same _bt_pagedel call). _bt_pagedel won't build a search stack for the page its caller passes whenever it happens to be one that an earlier interrupted VACUUM left half-dead (since there is no downlink to remove). We nevertheless asserted that page deletion could only build a search stack for that initial caller-passed page. To fix, remove the assertion, which spuriously failed when a half-dead leaf page was passed by the _bt_pagedel caller and its right sibling page was empty/had to be considered for deletion during the same call (necessitating building a search stack to locate the right sibling's downlink). Oversight in commit cd8c73a38, which refactored nbtree page deletion's INCOMPLETE_SPLIT check. Author: Peter Geoghegan Reported-by: Iliia Khaprov Discussion: https://postgr.es/m/CAFO+O_p5So4MXwQ1VEcL9+9vGgODu11TTWcYq9RDbxnira8o2w@mail.gmail.com Backpatch-through: 14 --- src/backend/access/nbtree/nbtpage.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c index 7c9412aad82..b0ffd15577b 100644 --- a/src/backend/access/nbtree/nbtpage.c +++ b/src/backend/access/nbtree/nbtpage.c @@ -1901,11 +1901,11 @@ _bt_pagedel(Relation rel, Buffer leafbuf, BTVacState *vstate) * left half of an incomplete split, but ensuring that it's not the * right half is more complicated. For that, we have to check that * the left sibling doesn't have its INCOMPLETE_SPLIT flag set using - * _bt_leftsib_splitflag(). On the first iteration, we temporarily - * release the lock on scanblkno/leafbuf, check the left sibling, and - * construct a search stack to scanblkno. On subsequent iterations, - * we know we stepped right from a page that passed these tests, so - * it's OK. + * _bt_leftsib_splitflag(). The first time we reach a page that isn't + * already half-dead (usually the scanblkno page), we temporarily + * release the lock on leafbuf, check the left sibling, and construct + * a search stack to leafbuf. On subsequent iterations, we know we + * stepped right from a page that passed these tests, so it's OK. */ if (P_RIGHTMOST(opaque) || P_ISROOT(opaque) || P_FIRSTDATAKEY(opaque) <= PageGetMaxOffsetNumber(page) || @@ -1960,7 +1960,6 @@ _bt_pagedel(Relation rel, Buffer leafbuf, BTVacState *vstate) * Check that the left sibling of leafbuf (if any) is not * marked with INCOMPLETE_SPLIT flag before proceeding */ - Assert(leafblkno == scanblkno); if (_bt_leftsib_splitflag(rel, leftsib, leafblkno)) { ReleaseBuffer(leafbuf);