From daaa9c92789f393404d7a5d3ec685cee46d9c3c8 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 15 Aug 2019 16:43:16 +0300 Subject: [PATCH v3] In amcheck nbtree do rightlink verification with lock coupling When doing nbtree verification we ignore rightlink-leftling invariant violations unless ShareLock is taken. To ensure detection of corruption this behaiovr is changed. With this commit we couple page locks and recheck links consistency again. --- contrib/amcheck/verify_nbtree.c | 41 +++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c index e4d501a85d..46dfe9be1f 100644 --- a/contrib/amcheck/verify_nbtree.c +++ b/contrib/amcheck/verify_nbtree.c @@ -143,6 +143,7 @@ static inline bool btree_index_mainfork_expected(Relation rel); static void bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace, bool readonly, bool heapallindexed, bool rootdescend); +static void bt_recheck_block_rightlink(BtreeCheckState *state, BlockNumber leftblockno); static BtreeLevel bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level); static void bt_target_page_check(BtreeCheckState *state); @@ -620,6 +621,42 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace, MemoryContextDelete(state->targetcontext); } +/* + * Verify the coherence of rightlink's leftlink under lock + */ +static void bt_recheck_block_rightlink(BtreeCheckState *state, BlockNumber leftblockno) +{ + Buffer lbuffer, rbuffer; + Page lpage,rpage; + BTPageOpaque lopaque, ropaque; + + /* Read and lock left block */ + lbuffer = ReadBufferExtended(state->rel, MAIN_FORKNUM, leftblockno, RBM_NORMAL, + state->checkstrategy); + LockBuffer(lbuffer, BT_READ); + lpage = BufferGetPage(lbuffer); + lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage); + + /* Read right block by following lopaque->btpo_next of left block */ + rbuffer = ReadBufferExtended(state->rel, MAIN_FORKNUM, lopaque->btpo_next, RBM_NORMAL, + state->checkstrategy); + /* Here we are going to couple locks on left block and right block */ + LockBuffer(rbuffer, BT_READ); + rpage = BufferGetPage(rbuffer); + ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage); + + if (ropaque->btpo_prev != leftblockno) + ereport(ERROR, + (errcode(ERRCODE_INDEX_CORRUPTED), + errmsg("left link/right link pair in index \"%s\" not in agreement", + RelationGetRelationName(state->rel)), + errdetail_internal("Block=%u left block=%u left link from block=%u.", + leftblockno, lopaque->btpo_next, ropaque->btpo_prev))); + + UnlockReleaseBuffer(lbuffer); + UnlockReleaseBuffer(rbuffer); +} + /* * Given a left-most block at some level, move right, verifying each page * individually (with more verification across pages for "readonly" @@ -778,6 +815,7 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level) /* * readonly mode can only ever land on live pages and half-dead pages, * so sibling pointers should always be in mutual agreement + * if not in readonly mode - we have to recheck links under lock */ if (state->readonly && opaque->btpo_prev != leftcurrent) ereport(ERROR, @@ -786,6 +824,9 @@ bt_check_level_from_leftmost(BtreeCheckState *state, BtreeLevel level) RelationGetRelationName(state->rel)), errdetail_internal("Block=%u left block=%u left link from block=%u.", current, leftcurrent, opaque->btpo_prev))); + else if (opaque->btpo_prev != leftcurrent && level.level == 0) + /* on leaf level - recheck rightlinks */ + bt_recheck_block_rightlink(state, leftcurrent); /* Check level, which must be valid for non-ignorable page */ if (level.level != opaque->btpo.level) -- 2.24.3 (Apple Git-128)