Skip to content

Commit f8cce4a

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 dd11188 commit f8cce4a

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

src/backend/access/nbtree/nbtinsert.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1157,10 +1157,30 @@ _bt_insertonpg(Relation rel,
11571157
* its post-split version is treated as an extra step in either the
11581158
* insert or page split critical section.
11591159
*/
1160-
Assert(P_ISLEAF(lpageop) && !ItemIdIsDead(itemid));
1161-
Assert(itup_key->heapkeyspace && itup_key->allequalimage);
1160+
Assert(P_ISLEAF(lpageop) &&
1161+
itup_key->heapkeyspace && itup_key->allequalimage);
11621162
oposting = (IndexTuple) PageGetItem(page, itemid);
11631163

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

src/backend/access/nbtree/nbtsearch.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,24 @@ _bt_binsrch_insert(Relation rel, BTInsertState insertstate)
530530
* infrequently.
531531
*/
532532
if (unlikely(result == 0 && key->scantid != NULL))
533+
{
534+
/*
535+
* postingoff should never be set more than once per leaf page
536+
* binary search. That would mean that there are duplicate table
537+
* TIDs in the index, which is never okay. Check for that here.
538+
*/
539+
if (insertstate->postingoff != 0)
540+
ereport(ERROR,
541+
(errcode(ERRCODE_INDEX_CORRUPTED),
542+
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\"",
543+
ItemPointerGetBlockNumber(key->scantid),
544+
ItemPointerGetOffsetNumber(key->scantid),
545+
low, stricthigh,
546+
BufferGetBlockNumber(insertstate->buf),
547+
RelationGetRelationName(rel))));
548+
533549
insertstate->postingoff = _bt_binsrch_posting(key, page, mid);
550+
}
534551
}
535552

536553
/*

0 commit comments

Comments
 (0)