}
#endif
+/*
+ * Helper function for heap_index_delete_tuples. Checks for index corruption
+ * involving an invalid TID in index AM caller's index page.
+ *
+ * This is an ideal place for these checks. The index AM must hold a buffer
+ * lock on the index page containing the TIDs we examine here, so we don't
+ * have to worry about concurrent VACUUMs at all. We can be sure that the
+ * index is corrupt when htid points directly to an LP_UNUSED item or
+ * heap-only tuple, which is not the case during standard index scans.
+ */
+static inline void
+index_delete_check_htid(TM_IndexDeleteOp *delstate,
+ Page page, OffsetNumber maxoff,
+ ItemPointer htid, TM_IndexStatus *istatus)
+{
+ OffsetNumber indexpagehoffnum = ItemPointerGetOffsetNumber(htid);
+ ItemId iid;
+
+ Assert(OffsetNumberIsValid(istatus->idxoffnum));
+
+ if (unlikely(indexpagehoffnum > maxoff))
+ ereport(ERROR,
+ (errcode(ERRCODE_INDEX_CORRUPTED),
+ errmsg_internal("heap tid from index tuple (%u,%u) points past end of heap page line pointer array at offset %u of block %u in index \"%s\"",
+ ItemPointerGetBlockNumber(htid),
+ indexpagehoffnum,
+ istatus->idxoffnum, delstate->iblknum,
+ RelationGetRelationName(delstate->irel))));
+
+ iid = PageGetItemId(page, indexpagehoffnum);
+ if (unlikely(!ItemIdIsUsed(iid)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INDEX_CORRUPTED),
+ errmsg_internal("heap tid from index tuple (%u,%u) points to unused heap page item at offset %u of block %u in index \"%s\"",
+ ItemPointerGetBlockNumber(htid),
+ indexpagehoffnum,
+ istatus->idxoffnum, delstate->iblknum,
+ RelationGetRelationName(delstate->irel))));
+
+ if (ItemIdHasStorage(iid))
+ {
+ HeapTupleHeader htup;
+
+ Assert(ItemIdIsNormal(iid));
+ htup = (HeapTupleHeader) PageGetItem(page, iid);
+
+ if (unlikely(HeapTupleHeaderIsHeapOnly(htup)))
+ ereport(ERROR,
+ (errcode(ERRCODE_INDEX_CORRUPTED),
+ errmsg_internal("heap tid from index tuple (%u,%u) points to heap-only tuple at offset %u of block %u in index \"%s\"",
+ ItemPointerGetBlockNumber(htid),
+ indexpagehoffnum,
+ istatus->idxoffnum, delstate->iblknum,
+ RelationGetRelationName(delstate->irel))));
+ }
+}
+
/*
* heapam implementation of tableam's index_delete_tuples interface.
*
maxoff = PageGetMaxOffsetNumber(page);
}
+ /*
+ * In passing, detect index corruption involving an index page with a
+ * TID that points to a location in the heap that couldn't possibly be
+ * correct. We only do this with actual TIDs from caller's index page
+ * (not items reached by traversing through a HOT chain).
+ */
+ index_delete_check_htid(delstate, page, maxoff, htid, istatus);
+
if (istatus->knowndeletable)
Assert(!delstate->bottomup && !istatus->promising);
else
Assert(nitems > 0);
+ delstate.irel = irel;
+ delstate.iblknum = BufferGetBlockNumber(ibuf);
delstate.bottomup = false;
delstate.bottomupfreespace = 0;
delstate.ndeltids = 0;
/* identify what the index tuples about to be deleted point to */
for (int i = 0; i < nitems; i++)
{
+ OffsetNumber offnum = itemnos[i];
ItemId iitemid;
- iitemid = PageGetItemId(ipage, itemnos[i]);
+ iitemid = PageGetItemId(ipage, offnum);
itup = (IndexTuple) PageGetItem(ipage, iitemid);
Assert(ItemIdIsDead(iitemid));
ItemPointerCopy(&itup->t_tid, &delstate.deltids[i].tid);
delstate.deltids[i].id = delstate.ndeltids;
- delstate.status[i].idxoffnum = InvalidOffsetNumber; /* unused */
+ delstate.status[i].idxoffnum = offnum;
delstate.status[i].knowndeletable = true; /* LP_DEAD-marked */
delstate.status[i].promising = false; /* unused */
delstate.status[i].freespace = 0; /* unused */