@@ -6364,6 +6364,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
6364
6364
*/
6365
6365
static TransactionId
6366
6366
FreezeMultiXactId (MultiXactId multi , uint16 t_infomask ,
6367
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6367
6368
TransactionId cutoff_xid , MultiXactId cutoff_multi ,
6368
6369
uint16 * flags )
6369
6370
{
@@ -6390,16 +6391,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6390
6391
* flags |= FRM_INVALIDATE_XMAX ;
6391
6392
return InvalidTransactionId ;
6392
6393
}
6394
+ else if (MultiXactIdPrecedes (multi , relminmxid ))
6395
+ ereport (ERROR ,
6396
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6397
+ errmsg_internal ("found multixact %u from before relminmxid %u" ,
6398
+ multi , relminmxid )));
6393
6399
else if (MultiXactIdPrecedes (multi , cutoff_multi ))
6394
6400
{
6395
6401
/*
6396
- * This old multi cannot possibly have members still running. If it
6397
- * was a locker only, it can be removed without any further
6398
- * consideration; but if it contained an update, we might need to
6399
- * preserve it.
6402
+ * This old multi cannot possibly have members still running, but
6403
+ * verify just in case. If it was a locker only, it can be removed
6404
+ * without any further consideration; but if it contained an update, we
6405
+ * might need to preserve it.
6400
6406
*/
6401
- Assert (!MultiXactIdIsRunning (multi ,
6402
- HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )));
6407
+ if (MultiXactIdIsRunning (multi ,
6408
+ HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )))
6409
+ ereport (ERROR ,
6410
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6411
+ errmsg_internal ("multixact %u from before cutoff %u found to be still running" ,
6412
+ multi , cutoff_multi )));
6413
+
6403
6414
if (HEAP_XMAX_IS_LOCKED_ONLY (t_infomask ))
6404
6415
{
6405
6416
* flags |= FRM_INVALIDATE_XMAX ;
@@ -6413,13 +6424,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6413
6424
/* wasn't only a lock, xid needs to be valid */
6414
6425
Assert (TransactionIdIsValid (xid ));
6415
6426
6427
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6428
+ ereport (ERROR ,
6429
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6430
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6431
+ xid , relfrozenxid )));
6432
+
6416
6433
/*
6417
6434
* If the xid is older than the cutoff, it has to have aborted,
6418
6435
* otherwise the tuple would have gotten pruned away.
6419
6436
*/
6420
6437
if (TransactionIdPrecedes (xid , cutoff_xid ))
6421
6438
{
6422
- Assert (!TransactionIdDidCommit (xid ));
6439
+ if (TransactionIdDidCommit (xid ))
6440
+ ereport (ERROR ,
6441
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6442
+ errmsg_internal ("cannot freeze committed update xid %u" , xid )));
6423
6443
* flags |= FRM_INVALIDATE_XMAX ;
6424
6444
xid = InvalidTransactionId ; /* not strictly necessary */
6425
6445
}
@@ -6491,6 +6511,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6491
6511
{
6492
6512
TransactionId xid = members [i ].xid ;
6493
6513
6514
+ Assert (TransactionIdIsValid (xid ));
6515
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6516
+ ereport (ERROR ,
6517
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6518
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6519
+ xid , relfrozenxid )));
6520
+
6494
6521
/*
6495
6522
* It's an update; should we keep it? If the transaction is known
6496
6523
* aborted or crashed then it's okay to ignore it, otherwise not.
@@ -6519,18 +6546,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6519
6546
update_committed = true;
6520
6547
update_xid = xid ;
6521
6548
}
6522
-
6523
- /*
6524
- * Not in progress, not committed -- must be aborted or crashed;
6525
- * we can ignore it.
6526
- */
6549
+ else
6550
+ {
6551
+ /*
6552
+ * Not in progress, not committed -- must be aborted or crashed;
6553
+ * we can ignore it.
6554
+ */
6555
+ }
6527
6556
6528
6557
/*
6529
6558
* Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6530
- * update Xid cannot possibly be older than the xid cutoff.
6559
+ * update Xid cannot possibly be older than the xid cutoff. The
6560
+ * presence of such a tuple would cause corruption, so be paranoid
6561
+ * and check.
6531
6562
*/
6532
- Assert (!TransactionIdIsValid (update_xid ) ||
6533
- !TransactionIdPrecedes (update_xid , cutoff_xid ));
6563
+ if (TransactionIdIsValid (update_xid ) &&
6564
+ TransactionIdPrecedes (update_xid , cutoff_xid ))
6565
+ ereport (ERROR ,
6566
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6567
+ errmsg_internal ("found update xid %u from before xid cutoff %u" ,
6568
+ update_xid , cutoff_xid )));
6534
6569
6535
6570
/*
6536
6571
* If we determined that it's an Xid corresponding to an update
@@ -6627,8 +6662,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6627
6662
* recovery. We really need to remove old xids.
6628
6663
*/
6629
6664
bool
6630
- heap_prepare_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
6631
- TransactionId cutoff_multi ,
6665
+ heap_prepare_freeze_tuple (HeapTupleHeader tuple ,
6666
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6667
+ TransactionId cutoff_xid , TransactionId cutoff_multi ,
6632
6668
xl_heap_freeze_tuple * frz , bool * totally_frozen_p )
6633
6669
{
6634
6670
bool changed = false;
@@ -6645,8 +6681,20 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6645
6681
xid = HeapTupleHeaderGetXmin (tuple );
6646
6682
if (TransactionIdIsNormal (xid ))
6647
6683
{
6684
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6685
+ ereport (ERROR ,
6686
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6687
+ errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
6688
+ xid , relfrozenxid )));
6689
+
6648
6690
if (TransactionIdPrecedes (xid , cutoff_xid ))
6649
6691
{
6692
+ if (!TransactionIdDidCommit (xid ))
6693
+ ereport (ERROR ,
6694
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6695
+ errmsg_internal ("uncommitted xmin %u from before xid cutoff %u needs to be frozen" ,
6696
+ xid , cutoff_xid )));
6697
+
6650
6698
frz -> t_infomask |= HEAP_XMIN_FROZEN ;
6651
6699
changed = true;
6652
6700
}
@@ -6671,6 +6719,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6671
6719
uint16 flags ;
6672
6720
6673
6721
newxmax = FreezeMultiXactId (xid , tuple -> t_infomask ,
6722
+ relfrozenxid , relminmxid ,
6674
6723
cutoff_xid , cutoff_multi , & flags );
6675
6724
6676
6725
if (flags & FRM_INVALIDATE_XMAX )
@@ -6720,8 +6769,28 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6720
6769
}
6721
6770
else if (TransactionIdIsNormal (xid ))
6722
6771
{
6772
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6773
+ ereport (ERROR ,
6774
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6775
+ errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
6776
+ xid , relfrozenxid )));
6777
+
6723
6778
if (TransactionIdPrecedes (xid , cutoff_xid ))
6779
+ {
6780
+ /*
6781
+ * If we freeze xmax, make absolutely sure that it's not an XID
6782
+ * that is important. (Note, a lock-only xmax can be removed
6783
+ * independent of committedness, since a committed lock holder has
6784
+ * released the lock).
6785
+ */
6786
+ if (!(tuple -> t_infomask & HEAP_XMAX_LOCK_ONLY ) &&
6787
+ TransactionIdDidCommit (xid ))
6788
+ ereport (ERROR ,
6789
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6790
+ errmsg_internal ("cannot freeze committed xmax %u" ,
6791
+ xid )));
6724
6792
freeze_xmax = true;
6793
+ }
6725
6794
else
6726
6795
totally_frozen = false;
6727
6796
}
@@ -6826,14 +6895,17 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
6826
6895
* Useful for callers like CLUSTER that perform their own WAL logging.
6827
6896
*/
6828
6897
bool
6829
- heap_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
6830
- TransactionId cutoff_multi )
6898
+ heap_freeze_tuple (HeapTupleHeader tuple ,
6899
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6900
+ TransactionId cutoff_xid , TransactionId cutoff_multi )
6831
6901
{
6832
6902
xl_heap_freeze_tuple frz ;
6833
6903
bool do_freeze ;
6834
6904
bool tuple_totally_frozen ;
6835
6905
6836
- do_freeze = heap_prepare_freeze_tuple (tuple , cutoff_xid , cutoff_multi ,
6906
+ do_freeze = heap_prepare_freeze_tuple (tuple ,
6907
+ relfrozenxid , relminmxid ,
6908
+ cutoff_xid , cutoff_multi ,
6837
6909
& frz , & tuple_totally_frozen );
6838
6910
6839
6911
/*
0 commit comments