@@ -5440,6 +5440,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
5440
5440
*/
5441
5441
static TransactionId
5442
5442
FreezeMultiXactId (MultiXactId multi , uint16 t_infomask ,
5443
+ TransactionId relfrozenxid , TransactionId relminmxid ,
5443
5444
TransactionId cutoff_xid , MultiXactId cutoff_multi ,
5444
5445
uint16 * flags )
5445
5446
{
@@ -5466,15 +5467,25 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5466
5467
* flags |= FRM_INVALIDATE_XMAX ;
5467
5468
return InvalidTransactionId ;
5468
5469
}
5470
+ else if (MultiXactIdPrecedes (multi , relminmxid ))
5471
+ ereport (ERROR ,
5472
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5473
+ errmsg_internal ("found multixact %u from before relminmxid %u" ,
5474
+ multi , relminmxid )));
5469
5475
else if (MultiXactIdPrecedes (multi , cutoff_multi ))
5470
5476
{
5471
5477
/*
5472
- * This old multi cannot possibly have members still running. If it
5473
- * was a locker only, it can be removed without any further
5474
- * consideration; but if it contained an update, we might need to
5475
- * preserve it.
5478
+ * This old multi cannot possibly have members still running, but
5479
+ * verify just in case. If it was a locker only, it can be removed
5480
+ * without any further consideration; but if it contained an update, we
5481
+ * might need to preserve it.
5476
5482
*/
5477
- Assert (!MultiXactIdIsRunning (multi ));
5483
+ if (MultiXactIdIsRunning (multi ))
5484
+ ereport (ERROR ,
5485
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5486
+ errmsg_internal ("multixact %u from before cutoff %u found to be still running" ,
5487
+ multi , cutoff_multi )));
5488
+
5478
5489
if (HEAP_XMAX_IS_LOCKED_ONLY (t_infomask ))
5479
5490
{
5480
5491
* flags |= FRM_INVALIDATE_XMAX ;
@@ -5488,13 +5499,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5488
5499
/* wasn't only a lock, xid needs to be valid */
5489
5500
Assert (TransactionIdIsValid (xid ));
5490
5501
5502
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
5503
+ ereport (ERROR ,
5504
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5505
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
5506
+ xid , relfrozenxid )));
5507
+
5491
5508
/*
5492
5509
* If the xid is older than the cutoff, it has to have aborted,
5493
5510
* otherwise the tuple would have gotten pruned away.
5494
5511
*/
5495
5512
if (TransactionIdPrecedes (xid , cutoff_xid ))
5496
5513
{
5497
- Assert (!TransactionIdDidCommit (xid ));
5514
+ if (TransactionIdDidCommit (xid ))
5515
+ ereport (ERROR ,
5516
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5517
+ errmsg_internal ("cannot freeze committed update xid %u" , xid )));
5498
5518
* flags |= FRM_INVALIDATE_XMAX ;
5499
5519
xid = InvalidTransactionId ; /* not strictly necessary */
5500
5520
}
@@ -5565,6 +5585,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5565
5585
{
5566
5586
TransactionId xid = members [i ].xid ;
5567
5587
5588
+ Assert (TransactionIdIsValid (xid ));
5589
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
5590
+ ereport (ERROR ,
5591
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5592
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
5593
+ xid , relfrozenxid )));
5594
+
5568
5595
/*
5569
5596
* It's an update; should we keep it? If the transaction is known
5570
5597
* aborted then it's okay to ignore it, otherwise not. However,
@@ -5598,6 +5625,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5598
5625
Assert (!TransactionIdIsValid (update_xid ));
5599
5626
update_xid = xid ;
5600
5627
}
5628
+ else
5629
+ {
5630
+ /*
5631
+ * Not in progress, not committed -- must be aborted or crashed;
5632
+ * we can ignore it.
5633
+ */
5634
+ }
5635
+
5636
+ /*
5637
+ * Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
5638
+ * update Xid cannot possibly be older than the xid cutoff. The
5639
+ * presence of such a tuple would cause corruption, so be paranoid
5640
+ * and check.
5641
+ */
5642
+ if (TransactionIdIsValid (update_xid ) &&
5643
+ TransactionIdPrecedes (update_xid , cutoff_xid ))
5644
+ ereport (ERROR ,
5645
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5646
+ errmsg_internal ("found update xid %u from before xid cutoff %u" ,
5647
+ update_xid , cutoff_xid )));
5601
5648
5602
5649
/*
5603
5650
* If we determined that it's an Xid corresponding to an update
@@ -5709,8 +5756,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5709
5756
* recovery. We really need to remove old xids.
5710
5757
*/
5711
5758
bool
5712
- heap_prepare_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
5713
- TransactionId cutoff_multi ,
5759
+ heap_prepare_freeze_tuple (HeapTupleHeader tuple ,
5760
+ TransactionId relfrozenxid , TransactionId relminmxid ,
5761
+ TransactionId cutoff_xid , TransactionId cutoff_multi ,
5714
5762
xl_heap_freeze_tuple * frz )
5715
5763
5716
5764
{
@@ -5725,17 +5773,32 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
5725
5773
5726
5774
/* Process xmin */
5727
5775
xid = HeapTupleHeaderGetXmin (tuple );
5728
- if (TransactionIdIsNormal (xid ) &&
5729
- TransactionIdPrecedes (xid , cutoff_xid ))
5776
+ if (TransactionIdIsNormal (xid ))
5730
5777
{
5731
- frz -> frzflags |= XLH_FREEZE_XMIN ;
5778
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
5779
+ {
5780
+ ereport (ERROR ,
5781
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5782
+ errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
5783
+ xid , relfrozenxid )));
5784
+ }
5785
+ if (TransactionIdPrecedes (xid , cutoff_xid ))
5786
+ {
5787
+ if (!TransactionIdDidCommit (xid ))
5788
+ ereport (ERROR ,
5789
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5790
+ errmsg_internal ("uncommitted xmin %u from before xid cutoff %u needs to be frozen" ,
5791
+ xid , cutoff_xid )));
5732
5792
5733
- /*
5734
- * Might as well fix the hint bits too; usually XMIN_COMMITTED will
5735
- * already be set here, but there's a small chance not.
5736
- */
5737
- frz -> t_infomask |= HEAP_XMIN_COMMITTED ;
5738
- changed = true;
5793
+ frz -> frzflags |= XLH_FREEZE_XMIN ;
5794
+
5795
+ /*
5796
+ * Might as well fix the hint bits too; usually XMIN_COMMITTED will
5797
+ * already be set here, but there's a small chance not.
5798
+ */
5799
+ frz -> t_infomask |= HEAP_XMIN_COMMITTED ;
5800
+ changed = true;
5801
+ }
5739
5802
}
5740
5803
5741
5804
/*
@@ -5755,6 +5818,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
5755
5818
uint16 flags ;
5756
5819
5757
5820
newxmax = FreezeMultiXactId (xid , tuple -> t_infomask ,
5821
+ relfrozenxid , relminmxid ,
5758
5822
cutoff_xid , cutoff_multi , & flags );
5759
5823
5760
5824
if (flags & FRM_INVALIDATE_XMAX )
@@ -5800,10 +5864,30 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
5800
5864
Assert (flags & FRM_NOOP );
5801
5865
}
5802
5866
}
5803
- else if (TransactionIdIsNormal (xid ) &&
5804
- TransactionIdPrecedes (xid , cutoff_xid ))
5867
+ else if (TransactionIdIsNormal (xid ))
5805
5868
{
5806
- freeze_xmax = true;
5869
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
5870
+ ereport (ERROR ,
5871
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5872
+ errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
5873
+ xid , relfrozenxid )));
5874
+
5875
+ if (TransactionIdPrecedes (xid , cutoff_xid ))
5876
+ {
5877
+ /*
5878
+ * If we freeze xmax, make absolutely sure that it's not an XID
5879
+ * that is important. (Note, a lock-only xmax can be removed
5880
+ * independent of committedness, since a committed lock holder has
5881
+ * released the lock).
5882
+ */
5883
+ if (!(tuple -> t_infomask & HEAP_XMAX_LOCK_ONLY ) &&
5884
+ TransactionIdDidCommit (xid ))
5885
+ ereport (ERROR ,
5886
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5887
+ errmsg_internal ("cannot freeze committed xmax %u" ,
5888
+ xid )));
5889
+ freeze_xmax = true;
5890
+ }
5807
5891
}
5808
5892
5809
5893
if (freeze_xmax )
@@ -5900,13 +5984,16 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
5900
5984
* Useful for callers like CLUSTER that perform their own WAL logging.
5901
5985
*/
5902
5986
bool
5903
- heap_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
5904
- TransactionId cutoff_multi )
5987
+ heap_freeze_tuple (HeapTupleHeader tuple ,
5988
+ TransactionId relfrozenxid , TransactionId relminmxid ,
5989
+ TransactionId cutoff_xid , TransactionId cutoff_multi )
5905
5990
{
5906
5991
xl_heap_freeze_tuple frz ;
5907
5992
bool do_freeze ;
5908
5993
5909
- do_freeze = heap_prepare_freeze_tuple (tuple , cutoff_xid , cutoff_multi ,
5994
+ do_freeze = heap_prepare_freeze_tuple (tuple ,
5995
+ relfrozenxid , relminmxid ,
5996
+ cutoff_xid , cutoff_multi ,
5910
5997
& frz );
5911
5998
5912
5999
/*
0 commit comments