From a2950357675198285dfdbc974346bce12dbd3c55 Mon Sep 17 00:00:00 2001
From: Pavel Borisov <pashkin.elfe@gmail.com>
Date: Thu, 25 Apr 2024 15:06:36 +0400
Subject: [PATCH v1 4/5] Amcheck: Report error when next page to a leaf is not
 a leaf

This is a very unlikely condition during checking unique constraint,
meaning that index connectivity is violated badly and we shouldn't
continue checking to avoid neverending loops etc. So it's better
to  honestly throw an error.

Reported-by: Peter Geoghegan
---
 contrib/amcheck/verify_nbtree.c | 23 ++++++++++++++++-------
 1 file changed, 16 insertions(+), 7 deletions(-)

diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index d6f70206db..dfc9ed769f 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -1829,7 +1829,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;
@@ -1874,12 +1873,12 @@ 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))
 			{
-				elog(DEBUG2, "check cross page unique condition");
+				BlockNumber rightblock_number = topaque->btpo_next;
 
+				elog(DEBUG2, "check cross page unique condition");
 				/*
 				 * Make _bt_compare compare only index keys without heap TIDs.
 				 * rightkey->scantid is modified destructively but it is ok
@@ -1900,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);
-- 
2.34.1

