aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/access/heap/heapam.c65
-rw-r--r--src/backend/access/index/genam.c7
-rw-r--r--src/backend/access/nbtree/nbtdedup.c2
-rw-r--r--src/backend/access/nbtree/nbtinsert.c2
-rw-r--r--src/include/access/tableam.h2
5 files changed, 76 insertions, 2 deletions
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index fecb9728683..ec234a5e595 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -7253,6 +7253,63 @@ index_delete_prefetch_buffer(Relation rel,
#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.
*
* This helper function is called by index AMs during index tuple deletion.
@@ -7446,6 +7503,14 @@ heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
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
diff --git a/src/backend/access/index/genam.c b/src/backend/access/index/genam.c
index b93288a6fe6..64023eaea5d 100644
--- a/src/backend/access/index/genam.c
+++ b/src/backend/access/index/genam.c
@@ -303,6 +303,8 @@ index_compute_xid_horizon_for_tuples(Relation irel,
Assert(nitems > 0);
+ delstate.irel = irel;
+ delstate.iblknum = BufferGetBlockNumber(ibuf);
delstate.bottomup = false;
delstate.bottomupfreespace = 0;
delstate.ndeltids = 0;
@@ -312,16 +314,17 @@ index_compute_xid_horizon_for_tuples(Relation irel,
/* 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 */
diff --git a/src/backend/access/nbtree/nbtdedup.c b/src/backend/access/nbtree/nbtdedup.c
index 6401fce57b9..c88dc6eedbd 100644
--- a/src/backend/access/nbtree/nbtdedup.c
+++ b/src/backend/access/nbtree/nbtdedup.c
@@ -348,6 +348,8 @@ _bt_bottomupdel_pass(Relation rel, Buffer buf, Relation heapRel,
* concerning ourselves with avoiding work during the tableam call. Our
* role in costing the bottom-up deletion process is strictly advisory.
*/
+ delstate.irel = rel;
+ delstate.iblknum = BufferGetBlockNumber(buf);
delstate.bottomup = true;
delstate.bottomupfreespace = Max(BLCKSZ / 16, newitemsz);
delstate.ndeltids = 0;
diff --git a/src/backend/access/nbtree/nbtinsert.c b/src/backend/access/nbtree/nbtinsert.c
index ccddb037820..0fe8c709395 100644
--- a/src/backend/access/nbtree/nbtinsert.c
+++ b/src/backend/access/nbtree/nbtinsert.c
@@ -2810,6 +2810,8 @@ _bt_simpledel_pass(Relation rel, Buffer buffer, Relation heapRel,
&ndeadblocks);
/* Initialize tableam state that describes index deletion operation */
+ delstate.irel = rel;
+ delstate.iblknum = BufferGetBlockNumber(buffer);
delstate.bottomup = false;
delstate.bottomupfreespace = 0;
delstate.ndeltids = 0;
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index 4aff18215ba..808c144a914 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -220,6 +220,8 @@ typedef struct TM_IndexStatus
*/
typedef struct TM_IndexDeleteOp
{
+ Relation irel; /* Target index relation */
+ BlockNumber iblknum; /* Index block number (for error reports) */
bool bottomup; /* Bottom-up (not simple) deletion? */
int bottomupfreespace; /* Bottom-up space target */