*/
static TransactionId
FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
+ TransactionId relfrozenxid, TransactionId relminmxid,
TransactionId cutoff_xid, MultiXactId cutoff_multi,
uint16 *flags)
{
*flags |= FRM_INVALIDATE_XMAX;
return InvalidTransactionId;
}
+ else if (MultiXactIdPrecedes(multi, relminmxid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("found multixact %u from before relminmxid %u",
+ multi, relminmxid)));
else if (MultiXactIdPrecedes(multi, cutoff_multi))
{
/*
- * This old multi cannot possibly have members still running. If it
- * was a locker only, it can be removed without any further
- * consideration; but if it contained an update, we might need to
- * preserve it.
+ * This old multi cannot possibly have members still running, but
+ * verify just in case. If it was a locker only, it can be removed
+ * without any further consideration; but if it contained an update, we
+ * might need to preserve it.
*/
- Assert(!MultiXactIdIsRunning(multi));
+ if (MultiXactIdIsRunning(multi))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("multixact %u from before cutoff %u found to be still running",
+ multi, cutoff_multi)));
+
if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask))
{
*flags |= FRM_INVALIDATE_XMAX;
/* wasn't only a lock, xid needs to be valid */
Assert(TransactionIdIsValid(xid));
+ if (TransactionIdPrecedes(xid, relfrozenxid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("found update xid %u from before relfrozenxid %u",
+ xid, relfrozenxid)));
+
/*
* If the xid is older than the cutoff, it has to have aborted,
* otherwise the tuple would have gotten pruned away.
*/
if (TransactionIdPrecedes(xid, cutoff_xid))
{
- Assert(!TransactionIdDidCommit(xid));
+ if (TransactionIdDidCommit(xid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("cannot freeze committed update xid %u", xid)));
*flags |= FRM_INVALIDATE_XMAX;
xid = InvalidTransactionId; /* not strictly necessary */
}
{
TransactionId xid = members[i].xid;
+ Assert(TransactionIdIsValid(xid));
+ if (TransactionIdPrecedes(xid, relfrozenxid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("found update xid %u from before relfrozenxid %u",
+ xid, relfrozenxid)));
+
/*
* It's an update; should we keep it? If the transaction is known
* aborted then it's okay to ignore it, otherwise not. However,
Assert(!TransactionIdIsValid(update_xid));
update_xid = xid;
}
+ else
+ {
+ /*
+ * Not in progress, not committed -- must be aborted or crashed;
+ * we can ignore it.
+ */
+ }
+
+ /*
+ * Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
+ * update Xid cannot possibly be older than the xid cutoff. The
+ * presence of such a tuple would cause corruption, so be paranoid
+ * and check.
+ */
+ if (TransactionIdIsValid(update_xid) &&
+ TransactionIdPrecedes(update_xid, cutoff_xid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("found update xid %u from before xid cutoff %u",
+ update_xid, cutoff_xid)));
/*
* If we determined that it's an Xid corresponding to an update
* recovery. We really need to remove old xids.
*/
bool
-heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi,
+heap_prepare_freeze_tuple(HeapTupleHeader tuple,
+ TransactionId relfrozenxid, TransactionId relminmxid,
+ TransactionId cutoff_xid, TransactionId cutoff_multi,
xl_heap_freeze_tuple *frz)
{
/* Process xmin */
xid = HeapTupleHeaderGetXmin(tuple);
- if (TransactionIdIsNormal(xid) &&
- TransactionIdPrecedes(xid, cutoff_xid))
+ if (TransactionIdIsNormal(xid))
{
- frz->frzflags |= XLH_FREEZE_XMIN;
+ if (TransactionIdPrecedes(xid, relfrozenxid))
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("found xmin %u from before relfrozenxid %u",
+ xid, relfrozenxid)));
+ }
+ if (TransactionIdPrecedes(xid, cutoff_xid))
+ {
+ if (!TransactionIdDidCommit(xid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("uncommitted xmin %u from before xid cutoff %u needs to be frozen",
+ xid, cutoff_xid)));
- /*
- * Might as well fix the hint bits too; usually XMIN_COMMITTED will
- * already be set here, but there's a small chance not.
- */
- frz->t_infomask |= HEAP_XMIN_COMMITTED;
- changed = true;
+ frz->frzflags |= XLH_FREEZE_XMIN;
+
+ /*
+ * Might as well fix the hint bits too; usually XMIN_COMMITTED will
+ * already be set here, but there's a small chance not.
+ */
+ frz->t_infomask |= HEAP_XMIN_COMMITTED;
+ changed = true;
+ }
}
/*
uint16 flags;
newxmax = FreezeMultiXactId(xid, tuple->t_infomask,
+ relfrozenxid, relminmxid,
cutoff_xid, cutoff_multi, &flags);
if (flags & FRM_INVALIDATE_XMAX)
Assert(flags & FRM_NOOP);
}
}
- else if (TransactionIdIsNormal(xid) &&
- TransactionIdPrecedes(xid, cutoff_xid))
+ else if (TransactionIdIsNormal(xid))
{
- freeze_xmax = true;
+ if (TransactionIdPrecedes(xid, relfrozenxid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("found xmax %u from before relfrozenxid %u",
+ xid, relfrozenxid)));
+
+ if (TransactionIdPrecedes(xid, cutoff_xid))
+ {
+ /*
+ * 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).
+ */
+ if (!(tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) &&
+ TransactionIdDidCommit(xid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg_internal("cannot freeze committed xmax %u",
+ xid)));
+ freeze_xmax = true;
+ }
}
if (freeze_xmax)
* Useful for callers like CLUSTER that perform their own WAL logging.
*/
bool
-heap_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
- TransactionId cutoff_multi)
+heap_freeze_tuple(HeapTupleHeader tuple,
+ TransactionId relfrozenxid, TransactionId relminmxid,
+ TransactionId cutoff_xid, TransactionId cutoff_multi)
{
xl_heap_freeze_tuple frz;
bool do_freeze;
- do_freeze = heap_prepare_freeze_tuple(tuple, cutoff_xid, cutoff_multi,
+ do_freeze = heap_prepare_freeze_tuple(tuple,
+ relfrozenxid, relminmxid,
+ cutoff_xid, cutoff_multi,
&frz);
/*