@@ -6208,10 +6208,10 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6208
6208
* been pruned away instead, since updater XID is < OldestXmin).
6209
6209
* Just remove xmax.
6210
6210
*/
6211
- if (TransactionIdDidCommit (update_xact ))
6211
+ if (! TransactionIdDidAbort (update_xact ))
6212
6212
ereport (ERROR ,
6213
6213
(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" ,
6215
6215
multi , update_xact ,
6216
6216
cutoffs -> OldestXmin )));
6217
6217
* flags |= FRM_INVALIDATE_XMAX ;
@@ -6500,10 +6500,11 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
6500
6500
freeze_xmax = false;
6501
6501
TransactionId xid ;
6502
6502
6503
- frz -> frzflags = 0 ;
6503
+ frz -> xmax = HeapTupleHeaderGetRawXmax ( tuple ) ;
6504
6504
frz -> t_infomask2 = tuple -> t_infomask2 ;
6505
6505
frz -> t_infomask = tuple -> t_infomask ;
6506
- frz -> xmax = HeapTupleHeaderGetRawXmax (tuple );
6506
+ frz -> frzflags = 0 ;
6507
+ frz -> checkflags = 0 ;
6507
6508
6508
6509
/*
6509
6510
* Process xmin, while keeping track of whether it's already frozen, or
@@ -6521,14 +6522,12 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
6521
6522
errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
6522
6523
xid , cutoffs -> relfrozenxid )));
6523
6524
6525
+ /* Will set freeze_xmin flags in freeze plan below */
6524
6526
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 )));
6530
6527
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 ;
6532
6531
}
6533
6532
6534
6533
/*
@@ -6551,7 +6550,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
6551
6550
}
6552
6551
6553
6552
/* Now process xmax */
6554
- xid = HeapTupleHeaderGetRawXmax ( tuple ) ;
6553
+ xid = frz -> xmax ;
6555
6554
if (tuple -> t_infomask & HEAP_XMAX_IS_MULTI )
6556
6555
{
6557
6556
/* Raw xmax is a MultiXactId */
@@ -6662,21 +6661,16 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
6662
6661
errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
6663
6662
xid , cutoffs -> relfrozenxid )));
6664
6663
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 ) ;
6667
6666
6668
6667
/*
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.)
6673
6671
*/
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 ;
6680
6674
}
6681
6675
else if (!TransactionIdIsValid (xid ))
6682
6676
{
@@ -6804,19 +6798,60 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer,
6804
6798
6805
6799
Assert (ntuples > 0 );
6806
6800
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 ;
6808
6813
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 ();
6810
6842
6811
6843
for (int i = 0 ; i < ntuples ; i ++ )
6812
6844
{
6845
+ HeapTupleFreeze * frz = tuples + i ;
6846
+ ItemId itemid = PageGetItemId (page , frz -> offset );
6813
6847
HeapTupleHeader htup ;
6814
- ItemId itemid = PageGetItemId (page , tuples [i ].offset );
6815
6848
6816
6849
htup = (HeapTupleHeader ) PageGetItem (page , itemid );
6817
- heap_execute_freeze_tuple (htup , & tuples [ i ] );
6850
+ heap_execute_freeze_tuple (htup , frz );
6818
6851
}
6819
6852
6853
+ MarkBufferDirty (buffer );
6854
+
6820
6855
/* Now WAL-log freezing if necessary */
6821
6856
if (RelationNeedsWAL (rel ))
6822
6857
{
0 commit comments