Skip to content

Commit a5213ad

Browse files
Further harden nbtree posting split code.
Add more defensive checks around posting list split code. These should detect corruption involving duplicate table TIDs earlier and more reliably than any existing check. Follow up to commit 8f72bba. Discussion: https://postgr.es/m/CAH2-WzkrSY_kjyd1_M5xJK1uM0govJXMxPn8JUSvwcUOiHuWVw@mail.gmail.com Backpatch: 13-, where nbtree deduplication was introduced.
1 parent eff6138 commit a5213ad

File tree

2 files changed

+38
-2
lines changed

2 files changed

+38
-2
lines changed

src/backend/access/nbtree/nbtinsert.c

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,10 +1164,29 @@ _bt_insertonpg(Relation rel,
11641164
* its post-split version is treated as an extra step in either the
11651165
* insert or page split critical section.
11661166
*/
1167-
Assert(isleaf && !ItemIdIsDead(itemid));
1168-
Assert(itup_key->heapkeyspace && itup_key->allequalimage);
1167+
Assert(isleaf && itup_key->heapkeyspace && itup_key->allequalimage);
11691168
oposting = (IndexTuple) PageGetItem(page, itemid);
11701169

1170+
/*
1171+
* postingoff value comes from earlier call to _bt_binsrch_posting().
1172+
* Its binary search might think that a plain tuple must be a posting
1173+
* list tuple that needs to be split. This can happen with corruption
1174+
* involving an existing plain tuple that is a duplicate of the new
1175+
* item, up to and including its table TID. Check for that here in
1176+
* passing.
1177+
*
1178+
* Also verify that our caller has made sure that the existing posting
1179+
* list tuple does not have its LP_DEAD bit set.
1180+
*/
1181+
if (!BTreeTupleIsPosting(oposting) || ItemIdIsDead(itemid))
1182+
ereport(ERROR,
1183+
(errcode(ERRCODE_INDEX_CORRUPTED),
1184+
errmsg_internal("table tid from new index tuple (%u,%u) overlaps with invalid duplicate tuple at offset %u of block %u in index \"%s\"",
1185+
ItemPointerGetBlockNumber(&itup->t_tid),
1186+
ItemPointerGetOffsetNumber(&itup->t_tid),
1187+
BufferGetBlockNumber(buf), newitemoff,
1188+
RelationGetRelationName(rel))));
1189+
11711190
/* use a mutable copy of itup as our itup from here on */
11721191
origitup = itup;
11731192
itup = CopyIndexTuple(origitup);

src/backend/access/nbtree/nbtsearch.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,24 @@ _bt_binsrch_insert(Relation rel, BTInsertState insertstate)
526526
* infrequently.
527527
*/
528528
if (unlikely(result == 0 && key->scantid != NULL))
529+
{
530+
/*
531+
* postingoff should never be set more than once per leaf page
532+
* binary search. That would mean that there are duplicate table
533+
* TIDs in the index, which is never okay. Check for that here.
534+
*/
535+
if (insertstate->postingoff != 0)
536+
ereport(ERROR,
537+
(errcode(ERRCODE_INDEX_CORRUPTED),
538+
errmsg_internal("table tid from new index tuple (%u,%u) cannot find insert offset between offsets %u and %u of block %u in index \"%s\"",
539+
ItemPointerGetBlockNumber(key->scantid),
540+
ItemPointerGetOffsetNumber(key->scantid),
541+
low, stricthigh,
542+
BufferGetBlockNumber(insertstate->buf),
543+
RelationGetRelationName(rel))));
544+
529545
insertstate->postingoff = _bt_binsrch_posting(key, page, mid);
546+
}
530547
}
531548

532549
/*

0 commit comments

Comments
 (0)