leftsib = opaque->btpo_next;
_bt_relbuf(rel, lbuf);
- /*
- * It'd be good to check for interrupts here, but it's not easy to
- * do so because a lock is always held. This block isn't
- * frequently reached, so hopefully the consequences of not
- * checking interrupts aren't too bad.
- */
-
if (leftsib == P_NONE)
{
elog(LOG, "no left sibling (concurrent deletion?) of block %u in \"%s\"",
}
return false;
}
+
+ CHECK_FOR_INTERRUPTS();
+
lbuf = _bt_getbuf(rel, leftsib, BT_WRITE);
page = BufferGetPage(lbuf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
rbuf = _bt_getbuf(rel, rightsib, BT_WRITE);
page = BufferGetPage(rbuf);
opaque = (BTPageOpaque) PageGetSpecialPointer(page);
+
+ /*
+ * Validate target's right sibling page. Its left link must point back to
+ * the target page.
+ */
if (opaque->btpo_prev != target)
- ereport(ERROR,
+ {
+ /*
+ * This is known to fail in the field; sibling link corruption is
+ * relatively common. Press on with vacuuming rather than just
+ * throwing an ERROR (same approach used for left-sibling's-right-link
+ * validation check a moment ago).
+ */
+ ereport(LOG,
(errcode(ERRCODE_INDEX_CORRUPTED),
errmsg_internal("right sibling's left-link doesn't match: "
- "block %u links to %u instead of expected %u in index \"%s\"",
- rightsib, opaque->btpo_prev, target,
- RelationGetRelationName(rel))));
+ "right sibling %u of target %u with leafblkno %u "
+ "and scanblkno %u spuriously links to non-target %u "
+ "on level %u of index \"%s\"",
+ rightsib, target, leafblkno,
+ scanblkno, opaque->btpo_prev,
+ targetlevel, RelationGetRelationName(rel))));
+
+ /* Must release all pins and locks on failure exit */
+ if (BufferIsValid(lbuf))
+ _bt_relbuf(rel, lbuf);
+ _bt_relbuf(rel, rbuf);
+ _bt_relbuf(rel, buf);
+ if (target != leafblkno)
+ _bt_relbuf(rel, leafbuf);
+
+ return false;
+ }
+
rightsib_is_rightmost = P_RIGHTMOST(opaque);
*rightsib_empty = (P_FIRSTDATAKEY(opaque) > PageGetMaxOffsetNumber(page));
* can't be half-dead because only an interrupted VACUUM process can
* leave pages in that state, so we'd definitely have dealt with it
* back when the page was the scanblkno page (half-dead pages are
- * always marked fully deleted by _bt_pagedel()). This assumes that
- * there can be only one vacuum process running at a time.
+ * always marked fully deleted by _bt_pagedel(), barring corruption).
*/
if (!opaque || !P_ISLEAF(opaque) || P_ISHALFDEAD(opaque))
{