From 97e5b0026fc276ab1bcde58ae98ae1fcd9c3acc3 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Thu, 23 May 2024 02:13:43 +0300 Subject: [PATCH] amcheck: Report an error when the next page to a leaf is not a leaf This is a very unlikely condition during checking a B-tree unique constraint, meaning that the index structure is violated badly, and we shouldn't continue checking to avoid endless loops, etc. So it's worth immediately throwing an error. Reported-by: Peter Geoghegan Discussion: https://postgr.es/m/CAH2-Wzk%2B2116uOXdOViA27SHcr31WKPgmjsxXLBs_aTxAeThzg%40mail.gmail.com Author: Pavel Borisov --- contrib/amcheck/verify_nbtree.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c index 5354b393b1..4347f9594c 100644 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@ -1831,7 +1831,6 @@ bt_target_page_check(BtreeCheckState *state) if (offset == max) { BTScanInsert rightkey; - BlockNumber rightblock_number; /* first offset on a right index page (log only) */ OffsetNumber rightfirstoffset = InvalidOffsetNumber; @@ -1876,10 +1875,11 @@ bt_target_page_check(BtreeCheckState *state) * If index has unique constraint make sure that no more than one * found equal items is visible. */ - rightblock_number = topaque->btpo_next; if (state->checkunique && state->indexinfo->ii_Unique && - rightkey && P_ISLEAF(topaque) && rightblock_number != P_NONE) + rightkey && P_ISLEAF(topaque) && !P_RIGHTMOST(topaque)) { + BlockNumber rightblock_number = topaque->btpo_next; + elog(DEBUG2, "check cross page unique condition"); /* @@ -1899,9 +1899,19 @@ bt_target_page_check(BtreeCheckState *state) rightblock_number); topaque = BTPageGetOpaque(rightpage); - if (P_IGNORE(topaque) || !P_ISLEAF(topaque)) - break; - + if (P_IGNORE(topaque)) + { + if (unlikely(!P_ISLEAF(topaque))) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("right block of leaf block is non-leaf for index \"%s\"", + RelationGetRelationName(state->rel)), + errdetail_internal("Block=%u page lsn=%X/%X.", + state->targetblock, + LSN_FORMAT_ARGS(state->targetlsn)))); + else + break; + } itemid = PageGetItemIdCareful(state, rightblock_number, rightpage, rightfirstoffset);