amcheck: Report an error when the next page to a leaf is not a leaf
authorAlexander Korotkov <[email protected]>
Wed, 22 May 2024 23:13:43 +0000 (02:13 +0300)
committerAlexander Korotkov <[email protected]>
Wed, 22 May 2024 23:13:43 +0000 (02:13 +0300)
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

index 5354b393b11513dc82bb0827d3630d0e398568ba..4347f9594c9c95940da9604253d9a05a7b24cbce 100644 (file)
@@ -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);