* been pruned away instead, since updater XID is < OldestXmin).
* Just remove xmax.
*/
- if (TransactionIdDidCommit(update_xact))
+ if (!TransactionIdDidAbort(update_xact))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("multixact %u contains committed update XID %u from before removable cutoff %u",
+ errmsg_internal("multixact %u contains non-aborted update XID %u from before removable cutoff %u",
multi, update_xact,
cutoffs->OldestXmin)));
*flags |= FRM_INVALIDATE_XMAX;
freeze_xmax = false;
TransactionId xid;
- frz->frzflags = 0;
+ frz->xmax = HeapTupleHeaderGetRawXmax(tuple);
frz->t_infomask2 = tuple->t_infomask2;
frz->t_infomask = tuple->t_infomask;
- frz->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ frz->frzflags = 0;
+ frz->checkflags = 0;
/*
* Process xmin, while keeping track of whether it's already frozen, or
errmsg_internal("found xmin %u from before relfrozenxid %u",
xid, cutoffs->relfrozenxid)));
+ /* Will set freeze_xmin flags in freeze plan below */
freeze_xmin = TransactionIdPrecedes(xid, cutoffs->OldestXmin);
- if (freeze_xmin && !TransactionIdDidCommit(xid))
- ereport(ERROR,
- (errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("uncommitted xmin %u from before xid cutoff %u needs to be frozen",
- xid, cutoffs->OldestXmin)));
- /* Will set freeze_xmin flags in freeze plan below */
+ /* Verify that xmin committed if and when freeze plan is executed */
+ if (freeze_xmin)
+ frz->checkflags |= HEAP_FREEZE_CHECK_XMIN_COMMITTED;
}
/*
}
/* Now process xmax */
- xid = HeapTupleHeaderGetRawXmax(tuple);
+ xid = frz->xmax;
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
/* Raw xmax is a MultiXactId */
errmsg_internal("found xmax %u from before relfrozenxid %u",
xid, cutoffs->relfrozenxid)));
- if (TransactionIdPrecedes(xid, cutoffs->OldestXmin))
- freeze_xmax = true;
+ /* Will set freeze_xmax flags in freeze plan below */
+ freeze_xmax = TransactionIdPrecedes(xid, cutoffs->OldestXmin);
/*
- * If we freeze xmax, make absolutely sure that it's not an XID that
- * is important. (Note, a lock-only xmax can be removed independent
- * of committedness, since a committed lock holder has released the
- * lock).
+ * Verify that xmax aborted if and when freeze plan is executed,
+ * provided it's from an update. (A lock-only xmax can be removed
+ * independent of this, since the lock is released at xact end.)
*/
- if (freeze_xmax && !HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) &&
- TransactionIdDidCommit(xid))
- ereport(ERROR,
- (errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("cannot freeze committed xmax %u",
- xid)));
+ if (freeze_xmax && !HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
+ frz->checkflags |= HEAP_FREEZE_CHECK_XMAX_ABORTED;
}
else if (!TransactionIdIsValid(xid))
{
Assert(ntuples > 0);
- START_CRIT_SECTION();
+ /*
+ * Perform xmin/xmax XID status sanity checks before critical section.
+ *
+ * heap_prepare_freeze_tuple doesn't perform these checks directly because
+ * pg_xact lookups are relatively expensive. They shouldn't be repeated
+ * by successive VACUUMs that each decide against freezing the same page.
+ */
+ for (int i = 0; i < ntuples; i++)
+ {
+ HeapTupleFreeze *frz = tuples + i;
+ ItemId itemid = PageGetItemId(page, frz->offset);
+ HeapTupleHeader htup;
- MarkBufferDirty(buffer);
+ htup = (HeapTupleHeader) PageGetItem(page, itemid);
+
+ /* Deliberately avoid relying on tuple hint bits here */
+ if (frz->checkflags & HEAP_FREEZE_CHECK_XMIN_COMMITTED)
+ {
+ TransactionId xmin = HeapTupleHeaderGetRawXmin(htup);
+
+ Assert(!HeapTupleHeaderXminFrozen(htup));
+ if (unlikely(!TransactionIdDidCommit(xmin)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("uncommitted xmin %u needs to be frozen",
+ xmin)));
+ }
+ if (frz->checkflags & HEAP_FREEZE_CHECK_XMAX_ABORTED)
+ {
+ TransactionId xmax = HeapTupleHeaderGetRawXmax(htup);
+
+ Assert(TransactionIdIsNormal(xmax));
+ if (unlikely(!TransactionIdDidAbort(xmax)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("cannot freeze non-aborted xmax %u",
+ xmax)));
+ }
+ }
+
+ START_CRIT_SECTION();
for (int i = 0; i < ntuples; i++)
{
+ HeapTupleFreeze *frz = tuples + i;
+ ItemId itemid = PageGetItemId(page, frz->offset);
HeapTupleHeader htup;
- ItemId itemid = PageGetItemId(page, tuples[i].offset);
htup = (HeapTupleHeader) PageGetItem(page, itemid);
- heap_execute_freeze_tuple(htup, &tuples[i]);
+ heap_execute_freeze_tuple(htup, frz);
}
+ MarkBufferDirty(buffer);
+
/* Now WAL-log freezing if necessary */
if (RelationNeedsWAL(rel))
{