@@ -5638,6 +5638,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
5638
5638
*/
5639
5639
static TransactionId
5640
5640
FreezeMultiXactId (MultiXactId multi , uint16 t_infomask ,
5641
+ TransactionId relfrozenxid , TransactionId relminmxid ,
5641
5642
TransactionId cutoff_xid , MultiXactId cutoff_multi ,
5642
5643
uint16 * flags )
5643
5644
{
@@ -5664,15 +5665,25 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5664
5665
* flags |= FRM_INVALIDATE_XMAX ;
5665
5666
return InvalidTransactionId ;
5666
5667
}
5668
+ else if (MultiXactIdPrecedes (multi , relminmxid ))
5669
+ ereport (ERROR ,
5670
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5671
+ errmsg_internal ("found multixact %u from before relminmxid %u" ,
5672
+ multi , relminmxid )));
5667
5673
else if (MultiXactIdPrecedes (multi , cutoff_multi ))
5668
5674
{
5669
5675
/*
5670
- * This old multi cannot possibly have members still running. If it
5671
- * was a locker only, it can be removed without any further
5672
- * consideration; but if it contained an update, we might need to
5673
- * preserve it.
5676
+ * This old multi cannot possibly have members still running, but
5677
+ * verify just in case. If it was a locker only, it can be removed
5678
+ * without any further consideration; but if it contained an update, we
5679
+ * might need to preserve it.
5674
5680
*/
5675
- Assert (!MultiXactIdIsRunning (multi ));
5681
+ if (MultiXactIdIsRunning (multi ))
5682
+ ereport (ERROR ,
5683
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5684
+ errmsg_internal ("multixact %u from before cutoff %u found to be still running" ,
5685
+ multi , cutoff_multi )));
5686
+
5676
5687
if (HEAP_XMAX_IS_LOCKED_ONLY (t_infomask ))
5677
5688
{
5678
5689
* flags |= FRM_INVALIDATE_XMAX ;
@@ -5686,13 +5697,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5686
5697
/* wasn't only a lock, xid needs to be valid */
5687
5698
Assert (TransactionIdIsValid (xid ));
5688
5699
5700
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
5701
+ ereport (ERROR ,
5702
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5703
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
5704
+ xid , relfrozenxid )));
5705
+
5689
5706
/*
5690
5707
* If the xid is older than the cutoff, it has to have aborted,
5691
5708
* otherwise the tuple would have gotten pruned away.
5692
5709
*/
5693
5710
if (TransactionIdPrecedes (xid , cutoff_xid ))
5694
5711
{
5695
- Assert (!TransactionIdDidCommit (xid ));
5712
+ if (TransactionIdDidCommit (xid ))
5713
+ ereport (ERROR ,
5714
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5715
+ errmsg_internal ("cannot freeze committed update xid %u" , xid )));
5696
5716
* flags |= FRM_INVALIDATE_XMAX ;
5697
5717
xid = InvalidTransactionId ; /* not strictly necessary */
5698
5718
}
@@ -5763,6 +5783,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5763
5783
{
5764
5784
TransactionId xid = members [i ].xid ;
5765
5785
5786
+ Assert (TransactionIdIsValid (xid ));
5787
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
5788
+ ereport (ERROR ,
5789
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5790
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
5791
+ xid , relfrozenxid )));
5792
+
5766
5793
/*
5767
5794
* It's an update; should we keep it? If the transaction is known
5768
5795
* aborted then it's okay to ignore it, otherwise not. However,
@@ -5796,6 +5823,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5796
5823
Assert (!TransactionIdIsValid (update_xid ));
5797
5824
update_xid = xid ;
5798
5825
}
5826
+ else
5827
+ {
5828
+ /*
5829
+ * Not in progress, not committed -- must be aborted or crashed;
5830
+ * we can ignore it.
5831
+ */
5832
+ }
5833
+
5834
+ /*
5835
+ * Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
5836
+ * update Xid cannot possibly be older than the xid cutoff. The
5837
+ * presence of such a tuple would cause corruption, so be paranoid
5838
+ * and check.
5839
+ */
5840
+ if (TransactionIdIsValid (update_xid ) &&
5841
+ TransactionIdPrecedes (update_xid , cutoff_xid ))
5842
+ ereport (ERROR ,
5843
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5844
+ errmsg_internal ("found update xid %u from before xid cutoff %u" ,
5845
+ update_xid , cutoff_xid )));
5799
5846
5800
5847
/*
5801
5848
* If we determined that it's an Xid corresponding to an update
@@ -5907,8 +5954,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
5907
5954
* recovery. We really need to remove old xids.
5908
5955
*/
5909
5956
bool
5910
- heap_prepare_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
5911
- TransactionId cutoff_multi ,
5957
+ heap_prepare_freeze_tuple (HeapTupleHeader tuple ,
5958
+ TransactionId relfrozenxid , TransactionId relminmxid ,
5959
+ TransactionId cutoff_xid , TransactionId cutoff_multi ,
5912
5960
xl_heap_freeze_tuple * frz )
5913
5961
5914
5962
{
@@ -5923,11 +5971,25 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
5923
5971
5924
5972
/* Process xmin */
5925
5973
xid = HeapTupleHeaderGetXmin (tuple );
5926
- if (TransactionIdIsNormal (xid ) &&
5927
- TransactionIdPrecedes (xid , cutoff_xid ))
5974
+ if (TransactionIdIsNormal (xid ))
5928
5975
{
5929
- frz -> t_infomask |= HEAP_XMIN_FROZEN ;
5930
- changed = true;
5976
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
5977
+ ereport (ERROR ,
5978
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5979
+ errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
5980
+ xid , relfrozenxid )));
5981
+
5982
+ if (TransactionIdPrecedes (xid , cutoff_xid ))
5983
+ {
5984
+ if (!TransactionIdDidCommit (xid ))
5985
+ ereport (ERROR ,
5986
+ (errcode (ERRCODE_DATA_CORRUPTED ),
5987
+ errmsg_internal ("uncommitted xmin %u from before xid cutoff %u needs to be frozen" ,
5988
+ xid , cutoff_xid )));
5989
+
5990
+ frz -> t_infomask |= HEAP_XMIN_FROZEN ;
5991
+ changed = true;
5992
+ }
5931
5993
}
5932
5994
5933
5995
/*
@@ -5947,6 +6009,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
5947
6009
uint16 flags ;
5948
6010
5949
6011
newxmax = FreezeMultiXactId (xid , tuple -> t_infomask ,
6012
+ relfrozenxid , relminmxid ,
5950
6013
cutoff_xid , cutoff_multi , & flags );
5951
6014
5952
6015
if (flags & FRM_INVALIDATE_XMAX )
@@ -5992,10 +6055,30 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
5992
6055
Assert (flags & FRM_NOOP );
5993
6056
}
5994
6057
}
5995
- else if (TransactionIdIsNormal (xid ) &&
5996
- TransactionIdPrecedes (xid , cutoff_xid ))
6058
+ else if (TransactionIdIsNormal (xid ))
5997
6059
{
5998
- freeze_xmax = true;
6060
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6061
+ ereport (ERROR ,
6062
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6063
+ errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
6064
+ xid , relfrozenxid )));
6065
+
6066
+ if (TransactionIdPrecedes (xid , cutoff_xid ))
6067
+ {
6068
+ /*
6069
+ * If we freeze xmax, make absolutely sure that it's not an XID
6070
+ * that is important. (Note, a lock-only xmax can be removed
6071
+ * independent of committedness, since a committed lock holder has
6072
+ * released the lock).
6073
+ */
6074
+ if (!(tuple -> t_infomask & HEAP_XMAX_LOCK_ONLY ) &&
6075
+ TransactionIdDidCommit (xid ))
6076
+ ereport (ERROR ,
6077
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6078
+ errmsg_internal ("cannot freeze committed xmax %u" ,
6079
+ xid )));
6080
+ freeze_xmax = true;
6081
+ }
5999
6082
}
6000
6083
6001
6084
if (freeze_xmax )
@@ -6089,13 +6172,16 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
6089
6172
* Useful for callers like CLUSTER that perform their own WAL logging.
6090
6173
*/
6091
6174
bool
6092
- heap_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
6093
- TransactionId cutoff_multi )
6175
+ heap_freeze_tuple (HeapTupleHeader tuple ,
6176
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6177
+ TransactionId cutoff_xid , TransactionId cutoff_multi )
6094
6178
{
6095
6179
xl_heap_freeze_tuple frz ;
6096
6180
bool do_freeze ;
6097
6181
6098
- do_freeze = heap_prepare_freeze_tuple (tuple , cutoff_xid , cutoff_multi ,
6182
+ do_freeze = heap_prepare_freeze_tuple (tuple ,
6183
+ relfrozenxid , relminmxid ,
6184
+ cutoff_xid , cutoff_multi ,
6099
6185
& frz );
6100
6186
6101
6187
/*
0 commit comments