@@ -6208,10 +6208,10 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
62086208 * been pruned away instead, since updater XID is < OldestXmin).
62096209 * Just remove xmax.
62106210 */
6211- if (TransactionIdDidCommit (update_xact ))
6211+ if (! TransactionIdDidAbort (update_xact ))
62126212 ereport (ERROR ,
62136213 (errcode (ERRCODE_DATA_CORRUPTED ),
6214- errmsg_internal ("multixact %u contains committed update XID %u from before removable cutoff %u" ,
6214+ errmsg_internal ("multixact %u contains non-aborted update XID %u from before removable cutoff %u" ,
62156215 multi , update_xact ,
62166216 cutoffs -> OldestXmin )));
62176217 * flags |= FRM_INVALIDATE_XMAX ;
@@ -6500,10 +6500,11 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
65006500 freeze_xmax = false;
65016501 TransactionId xid ;
65026502
6503- frz -> frzflags = 0 ;
6503+ frz -> xmax = HeapTupleHeaderGetRawXmax ( tuple ) ;
65046504 frz -> t_infomask2 = tuple -> t_infomask2 ;
65056505 frz -> t_infomask = tuple -> t_infomask ;
6506- frz -> xmax = HeapTupleHeaderGetRawXmax (tuple );
6506+ frz -> frzflags = 0 ;
6507+ frz -> checkflags = 0 ;
65076508
65086509 /*
65096510 * Process xmin, while keeping track of whether it's already frozen, or
@@ -6521,14 +6522,12 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
65216522 errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
65226523 xid , cutoffs -> relfrozenxid )));
65236524
6525+ /* Will set freeze_xmin flags in freeze plan below */
65246526 freeze_xmin = TransactionIdPrecedes (xid , cutoffs -> OldestXmin );
6525- if (freeze_xmin && !TransactionIdDidCommit (xid ))
6526- ereport (ERROR ,
6527- (errcode (ERRCODE_DATA_CORRUPTED ),
6528- errmsg_internal ("uncommitted xmin %u from before xid cutoff %u needs to be frozen" ,
6529- xid , cutoffs -> OldestXmin )));
65306527
6531- /* Will set freeze_xmin flags in freeze plan below */
6528+ /* Verify that xmin committed if and when freeze plan is executed */
6529+ if (freeze_xmin )
6530+ frz -> checkflags |= HEAP_FREEZE_CHECK_XMIN_COMMITTED ;
65326531 }
65336532
65346533 /*
@@ -6551,7 +6550,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
65516550 }
65526551
65536552 /* Now process xmax */
6554- xid = HeapTupleHeaderGetRawXmax ( tuple ) ;
6553+ xid = frz -> xmax ;
65556554 if (tuple -> t_infomask & HEAP_XMAX_IS_MULTI )
65566555 {
65576556 /* Raw xmax is a MultiXactId */
@@ -6662,21 +6661,16 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
66626661 errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
66636662 xid , cutoffs -> relfrozenxid )));
66646663
6665- if ( TransactionIdPrecedes ( xid , cutoffs -> OldestXmin ))
6666- freeze_xmax = true ;
6664+ /* Will set freeze_xmax flags in freeze plan below */
6665+ freeze_xmax = TransactionIdPrecedes ( xid , cutoffs -> OldestXmin ) ;
66676666
66686667 /*
6669- * If we freeze xmax, make absolutely sure that it's not an XID that
6670- * is important. (Note, a lock-only xmax can be removed independent
6671- * of committedness, since a committed lock holder has released the
6672- * lock).
6668+ * Verify that xmax aborted if and when freeze plan is executed,
6669+ * provided it's from an update. (A lock-only xmax can be removed
6670+ * independent of this, since the lock is released at xact end.)
66736671 */
6674- if (freeze_xmax && !HEAP_XMAX_IS_LOCKED_ONLY (tuple -> t_infomask ) &&
6675- TransactionIdDidCommit (xid ))
6676- ereport (ERROR ,
6677- (errcode (ERRCODE_DATA_CORRUPTED ),
6678- errmsg_internal ("cannot freeze committed xmax %u" ,
6679- xid )));
6672+ if (freeze_xmax && !HEAP_XMAX_IS_LOCKED_ONLY (tuple -> t_infomask ))
6673+ frz -> checkflags |= HEAP_FREEZE_CHECK_XMAX_ABORTED ;
66806674 }
66816675 else if (!TransactionIdIsValid (xid ))
66826676 {
@@ -6804,19 +6798,60 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer,
68046798
68056799 Assert (ntuples > 0 );
68066800
6807- START_CRIT_SECTION ();
6801+ /*
6802+ * Perform xmin/xmax XID status sanity checks before critical section.
6803+ *
6804+ * heap_prepare_freeze_tuple doesn't perform these checks directly because
6805+ * pg_xact lookups are relatively expensive. They shouldn't be repeated
6806+ * by successive VACUUMs that each decide against freezing the same page.
6807+ */
6808+ for (int i = 0 ; i < ntuples ; i ++ )
6809+ {
6810+ HeapTupleFreeze * frz = tuples + i ;
6811+ ItemId itemid = PageGetItemId (page , frz -> offset );
6812+ HeapTupleHeader htup ;
68086813
6809- MarkBufferDirty (buffer );
6814+ htup = (HeapTupleHeader ) PageGetItem (page , itemid );
6815+
6816+ /* Deliberately avoid relying on tuple hint bits here */
6817+ if (frz -> checkflags & HEAP_FREEZE_CHECK_XMIN_COMMITTED )
6818+ {
6819+ TransactionId xmin = HeapTupleHeaderGetRawXmin (htup );
6820+
6821+ Assert (!HeapTupleHeaderXminFrozen (htup ));
6822+ if (unlikely (!TransactionIdDidCommit (xmin )))
6823+ ereport (ERROR ,
6824+ (errcode (ERRCODE_DATA_CORRUPTED ),
6825+ errmsg_internal ("uncommitted xmin %u needs to be frozen" ,
6826+ xmin )));
6827+ }
6828+ if (frz -> checkflags & HEAP_FREEZE_CHECK_XMAX_ABORTED )
6829+ {
6830+ TransactionId xmax = HeapTupleHeaderGetRawXmax (htup );
6831+
6832+ Assert (TransactionIdIsNormal (xmax ));
6833+ if (unlikely (!TransactionIdDidAbort (xmax )))
6834+ ereport (ERROR ,
6835+ (errcode (ERRCODE_DATA_CORRUPTED ),
6836+ errmsg_internal ("cannot freeze non-aborted xmax %u" ,
6837+ xmax )));
6838+ }
6839+ }
6840+
6841+ START_CRIT_SECTION ();
68106842
68116843 for (int i = 0 ; i < ntuples ; i ++ )
68126844 {
6845+ HeapTupleFreeze * frz = tuples + i ;
6846+ ItemId itemid = PageGetItemId (page , frz -> offset );
68136847 HeapTupleHeader htup ;
6814- ItemId itemid = PageGetItemId (page , tuples [i ].offset );
68156848
68166849 htup = (HeapTupleHeader ) PageGetItem (page , itemid );
6817- heap_execute_freeze_tuple (htup , & tuples [ i ] );
6850+ heap_execute_freeze_tuple (htup , frz );
68186851 }
68196852
6853+ MarkBufferDirty (buffer );
6854+
68206855 /* Now WAL-log freezing if necessary */
68216856 if (RelationNeedsWAL (rel ))
68226857 {
0 commit comments