@@ -6357,6 +6357,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
63576357 */
63586358static TransactionId
63596359FreezeMultiXactId (MultiXactId multi ,uint16 t_infomask ,
6360+ TransactionId relfrozenxid ,TransactionId relminmxid ,
63606361TransactionId cutoff_xid ,MultiXactId cutoff_multi ,
63616362uint16 * flags )
63626363{
@@ -6383,16 +6384,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
63836384* flags |=FRM_INVALIDATE_XMAX ;
63846385return InvalidTransactionId ;
63856386}
6387+ else if (MultiXactIdPrecedes (multi ,relminmxid ))
6388+ ereport (ERROR ,
6389+ (errcode (ERRCODE_DATA_CORRUPTED ),
6390+ errmsg_internal ("found multixact %u from before relminmxid %u" ,
6391+ multi ,relminmxid )));
63866392else if (MultiXactIdPrecedes (multi ,cutoff_multi ))
63876393{
63886394/*
6389- * This old multi cannot possibly have members still running. If it
6390- * was a locker only, it can be removed without any further
6391- * consideration; but if it contained an update, we might need to
6392- * preserve it.
6395+ * This old multi cannot possibly have members still running, but
6396+ *verify just in case. If it was a locker only, it can be removed
6397+ *without any further consideration; but if it contained an update, we
6398+ *might need to preserve it.
63936399 */
6394- Assert (!MultiXactIdIsRunning (multi ,
6395- HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )));
6400+ if (MultiXactIdIsRunning (multi ,
6401+ HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )))
6402+ ereport (ERROR ,
6403+ (errcode (ERRCODE_DATA_CORRUPTED ),
6404+ errmsg_internal ("multixact %u from before cutoff %u found to be still running" ,
6405+ multi ,cutoff_multi )));
6406+
63966407if (HEAP_XMAX_IS_LOCKED_ONLY (t_infomask ))
63976408{
63986409* flags |=FRM_INVALIDATE_XMAX ;
@@ -6406,13 +6417,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
64066417/* wasn't only a lock, xid needs to be valid */
64076418Assert (TransactionIdIsValid (xid ));
64086419
6420+ if (TransactionIdPrecedes (xid ,relfrozenxid ))
6421+ ereport (ERROR ,
6422+ (errcode (ERRCODE_DATA_CORRUPTED ),
6423+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6424+ xid ,relfrozenxid )));
6425+
64096426/*
64106427 * If the xid is older than the cutoff, it has to have aborted,
64116428 * otherwise the tuple would have gotten pruned away.
64126429 */
64136430if (TransactionIdPrecedes (xid ,cutoff_xid ))
64146431{
6415- Assert (!TransactionIdDidCommit (xid ));
6432+ if (TransactionIdDidCommit (xid ))
6433+ ereport (ERROR ,
6434+ (errcode (ERRCODE_DATA_CORRUPTED ),
6435+ errmsg_internal ("cannot freeze committed update xid %u" ,xid )));
64166436* flags |=FRM_INVALIDATE_XMAX ;
64176437xid = InvalidTransactionId ;/* not strictly necessary */
64186438}
@@ -6484,6 +6504,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
64846504{
64856505TransactionId xid = members [i ].xid ;
64866506
6507+ Assert (TransactionIdIsValid (xid ));
6508+ if (TransactionIdPrecedes (xid ,relfrozenxid ))
6509+ ereport (ERROR ,
6510+ (errcode (ERRCODE_DATA_CORRUPTED ),
6511+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6512+ xid ,relfrozenxid )));
6513+
64876514/*
64886515 * It's an update; should we keep it? If the transaction is known
64896516 * aborted or crashed then it's okay to ignore it, otherwise not.
@@ -6512,18 +6539,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
65126539update_committed = true;
65136540update_xid = xid ;
65146541}
6515-
6516- /*
6517- * Not in progress, not committed -- must be aborted or crashed;
6518- * we can ignore it.
6519- */
6542+ else
6543+ {
6544+ /*
6545+ * Not in progress, not committed -- must be aborted or crashed;
6546+ * we can ignore it.
6547+ */
6548+ }
65206549
65216550/*
65226551 * Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6523- * update Xid cannot possibly be older than the xid cutoff.
6552+ * update Xid cannot possibly be older than the xid cutoff. The
6553+ * presence of such a tuple would cause corruption, so be paranoid
6554+ * and check.
65246555 */
6525- Assert (!TransactionIdIsValid (update_xid )||
6526- !TransactionIdPrecedes (update_xid ,cutoff_xid ));
6556+ if (TransactionIdIsValid (update_xid )&&
6557+ TransactionIdPrecedes (update_xid ,cutoff_xid ))
6558+ ereport (ERROR ,
6559+ (errcode (ERRCODE_DATA_CORRUPTED ),
6560+ errmsg_internal ("found update xid %u from before xid cutoff %u" ,
6561+ update_xid ,cutoff_xid )));
65276562
65286563/*
65296564 * If we determined that it's an Xid corresponding to an update
@@ -6620,8 +6655,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
66206655 * recovery. We really need to remove old xids.
66216656 */
66226657bool
6623- heap_prepare_freeze_tuple (HeapTupleHeader tuple ,TransactionId cutoff_xid ,
6624- TransactionId cutoff_multi ,
6658+ heap_prepare_freeze_tuple (HeapTupleHeader tuple ,
6659+ TransactionId relfrozenxid ,TransactionId relminmxid ,
6660+ TransactionId cutoff_xid ,TransactionId cutoff_multi ,
66256661xl_heap_freeze_tuple * frz ,bool * totally_frozen_p )
66266662{
66276663bool changed = false;
@@ -6638,8 +6674,20 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
66386674xid = HeapTupleHeaderGetXmin (tuple );
66396675if (TransactionIdIsNormal (xid ))
66406676{
6677+ if (TransactionIdPrecedes (xid ,relfrozenxid ))
6678+ ereport (ERROR ,
6679+ (errcode (ERRCODE_DATA_CORRUPTED ),
6680+ errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
6681+ xid ,relfrozenxid )));
6682+
66416683if (TransactionIdPrecedes (xid ,cutoff_xid ))
66426684{
6685+ if (!TransactionIdDidCommit (xid ))
6686+ ereport (ERROR ,
6687+ (errcode (ERRCODE_DATA_CORRUPTED ),
6688+ errmsg_internal ("uncommitted xmin %u from before xid cutoff %u needs to be frozen" ,
6689+ xid ,cutoff_xid )));
6690+
66436691frz -> t_infomask |=HEAP_XMIN_FROZEN ;
66446692changed = true;
66456693}
@@ -6664,6 +6712,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
66646712uint16 flags ;
66656713
66666714newxmax = FreezeMultiXactId (xid ,tuple -> t_infomask ,
6715+ relfrozenxid ,relminmxid ,
66676716cutoff_xid ,cutoff_multi ,& flags );
66686717
66696718if (flags & FRM_INVALIDATE_XMAX )
@@ -6713,8 +6762,28 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
67136762}
67146763else if (TransactionIdIsNormal (xid ))
67156764{
6765+ if (TransactionIdPrecedes (xid ,relfrozenxid ))
6766+ ereport (ERROR ,
6767+ (errcode (ERRCODE_DATA_CORRUPTED ),
6768+ errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
6769+ xid ,relfrozenxid )));
6770+
67166771if (TransactionIdPrecedes (xid ,cutoff_xid ))
6772+ {
6773+ /*
6774+ * If we freeze xmax, make absolutely sure that it's not an XID
6775+ * that is important. (Note, a lock-only xmax can be removed
6776+ * independent of committedness, since a committed lock holder has
6777+ * released the lock).
6778+ */
6779+ if (!(tuple -> t_infomask & HEAP_XMAX_LOCK_ONLY )&&
6780+ TransactionIdDidCommit (xid ))
6781+ ereport (ERROR ,
6782+ (errcode (ERRCODE_DATA_CORRUPTED ),
6783+ errmsg_internal ("cannot freeze committed xmax %u" ,
6784+ xid )));
67176785freeze_xmax = true;
6786+ }
67186787else
67196788totally_frozen = false;
67206789}
@@ -6819,14 +6888,17 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
68196888 * Useful for callers like CLUSTER that perform their own WAL logging.
68206889 */
68216890bool
6822- heap_freeze_tuple (HeapTupleHeader tuple ,TransactionId cutoff_xid ,
6823- TransactionId cutoff_multi )
6891+ heap_freeze_tuple (HeapTupleHeader tuple ,
6892+ TransactionId relfrozenxid ,TransactionId relminmxid ,
6893+ TransactionId cutoff_xid ,TransactionId cutoff_multi )
68246894{
68256895xl_heap_freeze_tuple frz ;
68266896bool do_freeze ;
68276897bool tuple_totally_frozen ;
68286898
6829- do_freeze = heap_prepare_freeze_tuple (tuple ,cutoff_xid ,cutoff_multi ,
6899+ do_freeze = heap_prepare_freeze_tuple (tuple ,
6900+ relfrozenxid ,relminmxid ,
6901+ cutoff_xid ,cutoff_multi ,
68306902& frz ,& tuple_totally_frozen );
68316903
68326904/*