@@ -1157,10 +1157,30 @@ _bt_insertonpg(Relation rel,
1157
1157
* its post-split version is treated as an extra step in either the
1158
1158
* insert or page split critical section.
1159
1159
*/
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 );
1162
1162
oposting = (IndexTuple ) PageGetItem (page , itemid );
1163
1163
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
+
1164
1184
/* use a mutable copy of itup as our itup from here on */
1165
1185
origitup = itup ;
1166
1186
itup = CopyIndexTuple (origitup );
0 commit comments