@@ -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 ,
63606361 TransactionId cutoff_xid , MultiXactId cutoff_multi ,
63616362 uint16 * flags )
63626363{
@@ -6383,16 +6384,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
63836384 * flags |= FRM_INVALIDATE_XMAX ;
63846385 return 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 )));
63866392 else 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+
63966407 if (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 */
64076418 Assert (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 */
64136430 if (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 ;
64176437 xid = InvalidTransactionId ; /* not strictly necessary */
64186438 }
@@ -6484,6 +6504,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
64846504 {
64856505 TransactionId 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,
65126539 update_committed = true;
65136540 update_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 ,
66256661 xl_heap_freeze_tuple * frz , bool * totally_frozen_p )
66266662{
66276663 bool changed = false;
@@ -6638,8 +6674,20 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
66386674 xid = HeapTupleHeaderGetXmin (tuple );
66396675 if (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+
66416683 if (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+
66436691 frz -> t_infomask |= HEAP_XMIN_FROZEN ;
66446692 changed = true;
66456693 }
@@ -6664,6 +6712,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
66646712 uint16 flags ;
66656713
66666714 newxmax = FreezeMultiXactId (xid , tuple -> t_infomask ,
6715+ relfrozenxid , relminmxid ,
66676716 cutoff_xid , cutoff_multi , & flags );
66686717
66696718 if (flags & FRM_INVALIDATE_XMAX )
@@ -6713,8 +6762,28 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
67136762 }
67146763 else 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+
67166771 if (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 )));
67176785 freeze_xmax = true;
6786+ }
67186787 else
67196788 totally_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{
68256895 xl_heap_freeze_tuple frz ;
68266896 bool do_freeze ;
68276897 bool 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 /*
0 commit comments