@@ -6357,6 +6357,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
6357
6357
*/
6358
6358
static TransactionId
6359
6359
FreezeMultiXactId (MultiXactId multi , uint16 t_infomask ,
6360
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6360
6361
TransactionId cutoff_xid , MultiXactId cutoff_multi ,
6361
6362
uint16 * flags )
6362
6363
{
@@ -6383,16 +6384,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6383
6384
* flags |= FRM_INVALIDATE_XMAX ;
6384
6385
return InvalidTransactionId ;
6385
6386
}
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 )));
6386
6392
else if (MultiXactIdPrecedes (multi , cutoff_multi ))
6387
6393
{
6388
6394
/*
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.
6393
6399
*/
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
+
6396
6407
if (HEAP_XMAX_IS_LOCKED_ONLY (t_infomask ))
6397
6408
{
6398
6409
* flags |= FRM_INVALIDATE_XMAX ;
@@ -6406,13 +6417,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6406
6417
/* wasn't only a lock, xid needs to be valid */
6407
6418
Assert (TransactionIdIsValid (xid ));
6408
6419
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
+
6409
6426
/*
6410
6427
* If the xid is older than the cutoff, it has to have aborted,
6411
6428
* otherwise the tuple would have gotten pruned away.
6412
6429
*/
6413
6430
if (TransactionIdPrecedes (xid , cutoff_xid ))
6414
6431
{
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 )));
6416
6436
* flags |= FRM_INVALIDATE_XMAX ;
6417
6437
xid = InvalidTransactionId ; /* not strictly necessary */
6418
6438
}
@@ -6484,6 +6504,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6484
6504
{
6485
6505
TransactionId xid = members [i ].xid ;
6486
6506
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
+
6487
6514
/*
6488
6515
* It's an update; should we keep it? If the transaction is known
6489
6516
* aborted or crashed then it's okay to ignore it, otherwise not.
@@ -6512,18 +6539,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6512
6539
update_committed = true;
6513
6540
update_xid = xid ;
6514
6541
}
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
+ }
6520
6549
6521
6550
/*
6522
6551
* 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.
6524
6555
*/
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 )));
6527
6562
6528
6563
/*
6529
6564
* If we determined that it's an Xid corresponding to an update
@@ -6620,8 +6655,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6620
6655
* recovery. We really need to remove old xids.
6621
6656
*/
6622
6657
bool
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 ,
6625
6661
xl_heap_freeze_tuple * frz , bool * totally_frozen_p )
6626
6662
{
6627
6663
bool changed = false;
@@ -6638,8 +6674,20 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6638
6674
xid = HeapTupleHeaderGetXmin (tuple );
6639
6675
if (TransactionIdIsNormal (xid ))
6640
6676
{
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
+
6641
6683
if (TransactionIdPrecedes (xid , cutoff_xid ))
6642
6684
{
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
+
6643
6691
frz -> t_infomask |= HEAP_XMIN_FROZEN ;
6644
6692
changed = true;
6645
6693
}
@@ -6664,6 +6712,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6664
6712
uint16 flags ;
6665
6713
6666
6714
newxmax = FreezeMultiXactId (xid , tuple -> t_infomask ,
6715
+ relfrozenxid , relminmxid ,
6667
6716
cutoff_xid , cutoff_multi , & flags );
6668
6717
6669
6718
if (flags & FRM_INVALIDATE_XMAX )
@@ -6713,8 +6762,28 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6713
6762
}
6714
6763
else if (TransactionIdIsNormal (xid ))
6715
6764
{
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
+
6716
6771
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 )));
6717
6785
freeze_xmax = true;
6786
+ }
6718
6787
else
6719
6788
totally_frozen = false;
6720
6789
}
@@ -6819,14 +6888,17 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
6819
6888
* Useful for callers like CLUSTER that perform their own WAL logging.
6820
6889
*/
6821
6890
bool
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 )
6824
6894
{
6825
6895
xl_heap_freeze_tuple frz ;
6826
6896
bool do_freeze ;
6827
6897
bool tuple_totally_frozen ;
6828
6898
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 ,
6830
6902
& frz , & tuple_totally_frozen );
6831
6903
6832
6904
/*
0 commit comments