@@ -6030,6 +6030,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
60306030 */
60316031static TransactionId
60326032FreezeMultiXactId (MultiXactId multi ,uint16 t_infomask ,
6033+ TransactionId relfrozenxid ,TransactionId relminmxid ,
60336034TransactionId cutoff_xid ,MultiXactId cutoff_multi ,
60346035uint16 * flags )
60356036{
@@ -6056,16 +6057,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
60566057* flags |=FRM_INVALIDATE_XMAX ;
60576058return InvalidTransactionId ;
60586059}
6060+ else if (MultiXactIdPrecedes (multi ,relminmxid ))
6061+ ereport (ERROR ,
6062+ (errcode (ERRCODE_DATA_CORRUPTED ),
6063+ errmsg_internal ("found multixact %u from before relminmxid %u" ,
6064+ multi ,relminmxid )));
60596065else if (MultiXactIdPrecedes (multi ,cutoff_multi ))
60606066{
60616067/*
6062- * This old multi cannot possibly have members still running. If it
6063- * was a locker only, it can be removed without any further
6064- * consideration; but if it contained an update, we might need to
6065- * preserve it.
6068+ * This old multi cannot possibly have members still running, but
6069+ *verify just in case. If it was a locker only, it can be removed
6070+ *without any further consideration; but if it contained an update, we
6071+ *might need to preserve it.
60666072 */
6067- Assert (!MultiXactIdIsRunning (multi ,
6068- HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )));
6073+ if (MultiXactIdIsRunning (multi ,
6074+ HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )))
6075+ ereport (ERROR ,
6076+ (errcode (ERRCODE_DATA_CORRUPTED ),
6077+ errmsg_internal ("multixact %u from before cutoff %u found to be still running" ,
6078+ multi ,cutoff_multi )));
6079+
60696080if (HEAP_XMAX_IS_LOCKED_ONLY (t_infomask ))
60706081{
60716082* flags |=FRM_INVALIDATE_XMAX ;
@@ -6079,13 +6090,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
60796090/* wasn't only a lock, xid needs to be valid */
60806091Assert (TransactionIdIsValid (xid ));
60816092
6093+ if (TransactionIdPrecedes (xid ,relfrozenxid ))
6094+ ereport (ERROR ,
6095+ (errcode (ERRCODE_DATA_CORRUPTED ),
6096+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6097+ xid ,relfrozenxid )));
6098+
60826099/*
60836100 * If the xid is older than the cutoff, it has to have aborted,
60846101 * otherwise the tuple would have gotten pruned away.
60856102 */
60866103if (TransactionIdPrecedes (xid ,cutoff_xid ))
60876104{
6088- Assert (!TransactionIdDidCommit (xid ));
6105+ if (TransactionIdDidCommit (xid ))
6106+ ereport (ERROR ,
6107+ (errcode (ERRCODE_DATA_CORRUPTED ),
6108+ errmsg_internal ("cannot freeze committed update xid %u" ,xid )));
60896109* flags |=FRM_INVALIDATE_XMAX ;
60906110xid = InvalidTransactionId ;/* not strictly necessary */
60916111}
@@ -6157,6 +6177,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
61576177{
61586178TransactionId xid = members [i ].xid ;
61596179
6180+ Assert (TransactionIdIsValid (xid ));
6181+ if (TransactionIdPrecedes (xid ,relfrozenxid ))
6182+ ereport (ERROR ,
6183+ (errcode (ERRCODE_DATA_CORRUPTED ),
6184+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6185+ xid ,relfrozenxid )));
6186+
61606187/*
61616188 * It's an update; should we keep it? If the transaction is known
61626189 * aborted or crashed then it's okay to ignore it, otherwise not.
@@ -6185,18 +6212,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
61856212update_committed = true;
61866213update_xid = xid ;
61876214}
6188-
6189- /*
6190- * Not in progress, not committed -- must be aborted or crashed;
6191- * we can ignore it.
6192- */
6215+ else
6216+ {
6217+ /*
6218+ * Not in progress, not committed -- must be aborted or crashed;
6219+ * we can ignore it.
6220+ */
6221+ }
61936222
61946223/*
61956224 * Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6196- * update Xid cannot possibly be older than the xid cutoff.
6225+ * update Xid cannot possibly be older than the xid cutoff. The
6226+ * presence of such a tuple would cause corruption, so be paranoid
6227+ * and check.
61976228 */
6198- Assert (!TransactionIdIsValid (update_xid )||
6199- !TransactionIdPrecedes (update_xid ,cutoff_xid ));
6229+ if (TransactionIdIsValid (update_xid )&&
6230+ TransactionIdPrecedes (update_xid ,cutoff_xid ))
6231+ ereport (ERROR ,
6232+ (errcode (ERRCODE_DATA_CORRUPTED ),
6233+ errmsg_internal ("found update xid %u from before xid cutoff %u" ,
6234+ update_xid ,cutoff_xid )));
62006235
62016236/*
62026237 * If we determined that it's an Xid corresponding to an update
@@ -6291,8 +6326,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
62916326 * recovery. We really need to remove old xids.
62926327 */
62936328bool
6294- heap_prepare_freeze_tuple (HeapTupleHeader tuple ,TransactionId cutoff_xid ,
6295- TransactionId cutoff_multi ,
6329+ heap_prepare_freeze_tuple (HeapTupleHeader tuple ,
6330+ TransactionId relfrozenxid ,TransactionId relminmxid ,
6331+ TransactionId cutoff_xid ,TransactionId cutoff_multi ,
62966332xl_heap_freeze_tuple * frz )
62976333
62986334{
@@ -6307,11 +6343,25 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
63076343
63086344/* Process xmin */
63096345xid = HeapTupleHeaderGetXmin (tuple );
6310- if (TransactionIdIsNormal (xid )&&
6311- TransactionIdPrecedes (xid ,cutoff_xid ))
6346+ if (TransactionIdIsNormal (xid ))
63126347{
6313- frz -> t_infomask |=HEAP_XMIN_FROZEN ;
6314- changed = true;
6348+ if (TransactionIdPrecedes (xid ,relfrozenxid ))
6349+ ereport (ERROR ,
6350+ (errcode (ERRCODE_DATA_CORRUPTED ),
6351+ errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
6352+ xid ,relfrozenxid )));
6353+
6354+ if (TransactionIdPrecedes (xid ,cutoff_xid ))
6355+ {
6356+ if (!TransactionIdDidCommit (xid ))
6357+ ereport (ERROR ,
6358+ (errcode (ERRCODE_DATA_CORRUPTED ),
6359+ errmsg_internal ("uncommitted xmin %u from before xid cutoff %u needs to be frozen" ,
6360+ xid ,cutoff_xid )));
6361+
6362+ frz -> t_infomask |=HEAP_XMIN_FROZEN ;
6363+ changed = true;
6364+ }
63156365}
63166366
63176367/*
@@ -6331,6 +6381,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
63316381uint16 flags ;
63326382
63336383newxmax = FreezeMultiXactId (xid ,tuple -> t_infomask ,
6384+ relfrozenxid ,relminmxid ,
63346385cutoff_xid ,cutoff_multi ,& flags );
63356386
63366387if (flags & FRM_INVALIDATE_XMAX )
@@ -6376,10 +6427,30 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
63766427Assert (flags & FRM_NOOP );
63776428}
63786429}
6379- else if (TransactionIdIsNormal (xid )&&
6380- TransactionIdPrecedes (xid ,cutoff_xid ))
6430+ else if (TransactionIdIsNormal (xid ))
63816431{
6382- freeze_xmax = true;
6432+ if (TransactionIdPrecedes (xid ,relfrozenxid ))
6433+ ereport (ERROR ,
6434+ (errcode (ERRCODE_DATA_CORRUPTED ),
6435+ errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
6436+ xid ,relfrozenxid )));
6437+
6438+ if (TransactionIdPrecedes (xid ,cutoff_xid ))
6439+ {
6440+ /*
6441+ * If we freeze xmax, make absolutely sure that it's not an XID
6442+ * that is important. (Note, a lock-only xmax can be removed
6443+ * independent of committedness, since a committed lock holder has
6444+ * released the lock).
6445+ */
6446+ if (!(tuple -> t_infomask & HEAP_XMAX_LOCK_ONLY )&&
6447+ TransactionIdDidCommit (xid ))
6448+ ereport (ERROR ,
6449+ (errcode (ERRCODE_DATA_CORRUPTED ),
6450+ errmsg_internal ("cannot freeze committed xmax %u" ,
6451+ xid )));
6452+ freeze_xmax = true;
6453+ }
63836454}
63846455
63856456if (freeze_xmax )
@@ -6473,13 +6544,16 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
64736544 * Useful for callers like CLUSTER that perform their own WAL logging.
64746545 */
64756546bool
6476- heap_freeze_tuple (HeapTupleHeader tuple ,TransactionId cutoff_xid ,
6477- TransactionId cutoff_multi )
6547+ heap_freeze_tuple (HeapTupleHeader tuple ,
6548+ TransactionId relfrozenxid ,TransactionId relminmxid ,
6549+ TransactionId cutoff_xid ,TransactionId cutoff_multi )
64786550{
64796551xl_heap_freeze_tuple frz ;
64806552bool do_freeze ;
64816553
6482- do_freeze = heap_prepare_freeze_tuple (tuple ,cutoff_xid ,cutoff_multi ,
6554+ do_freeze = heap_prepare_freeze_tuple (tuple ,
6555+ relfrozenxid ,relminmxid ,
6556+ cutoff_xid ,cutoff_multi ,
64836557& frz );
64846558
64856559/*