@@ -6338,6 +6338,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
6338
6338
*/
6339
6339
static TransactionId
6340
6340
FreezeMultiXactId (MultiXactId multi , uint16 t_infomask ,
6341
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6341
6342
TransactionId cutoff_xid , MultiXactId cutoff_multi ,
6342
6343
uint16 * flags )
6343
6344
{
@@ -6364,16 +6365,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6364
6365
* flags |= FRM_INVALIDATE_XMAX ;
6365
6366
return InvalidTransactionId ;
6366
6367
}
6368
+ else if (MultiXactIdPrecedes (multi , relminmxid ))
6369
+ ereport (ERROR ,
6370
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6371
+ errmsg_internal ("found multixact %u from before relminmxid %u" ,
6372
+ multi , relminmxid )));
6367
6373
else if (MultiXactIdPrecedes (multi , cutoff_multi ))
6368
6374
{
6369
6375
/*
6370
- * This old multi cannot possibly have members still running. If it
6371
- * was a locker only, it can be removed without any further
6372
- * consideration; but if it contained an update, we might need to
6373
- * preserve it.
6376
+ * This old multi cannot possibly have members still running, but
6377
+ * verify just in case. If it was a locker only, it can be removed
6378
+ * without any further consideration; but if it contained an update, we
6379
+ * might need to preserve it.
6374
6380
*/
6375
- Assert (!MultiXactIdIsRunning (multi ,
6376
- HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )));
6381
+ if (MultiXactIdIsRunning (multi ,
6382
+ HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )))
6383
+ ereport (ERROR ,
6384
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6385
+ errmsg_internal ("multixact %u from before cutoff %u found to be still running" ,
6386
+ multi , cutoff_multi )));
6387
+
6377
6388
if (HEAP_XMAX_IS_LOCKED_ONLY (t_infomask ))
6378
6389
{
6379
6390
* flags |= FRM_INVALIDATE_XMAX ;
@@ -6387,13 +6398,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6387
6398
/* wasn't only a lock, xid needs to be valid */
6388
6399
Assert (TransactionIdIsValid (xid ));
6389
6400
6401
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6402
+ ereport (ERROR ,
6403
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6404
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6405
+ xid , relfrozenxid )));
6406
+
6390
6407
/*
6391
6408
* If the xid is older than the cutoff, it has to have aborted,
6392
6409
* otherwise the tuple would have gotten pruned away.
6393
6410
*/
6394
6411
if (TransactionIdPrecedes (xid , cutoff_xid ))
6395
6412
{
6396
- Assert (!TransactionIdDidCommit (xid ));
6413
+ if (TransactionIdDidCommit (xid ))
6414
+ ereport (ERROR ,
6415
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6416
+ errmsg_internal ("cannot freeze committed update xid %u" , xid )));
6397
6417
* flags |= FRM_INVALIDATE_XMAX ;
6398
6418
xid = InvalidTransactionId ; /* not strictly necessary */
6399
6419
}
@@ -6465,6 +6485,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6465
6485
{
6466
6486
TransactionId xid = members [i ].xid ;
6467
6487
6488
+ Assert (TransactionIdIsValid (xid ));
6489
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6490
+ ereport (ERROR ,
6491
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6492
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6493
+ xid , relfrozenxid )));
6494
+
6468
6495
/*
6469
6496
* It's an update; should we keep it? If the transaction is known
6470
6497
* aborted or crashed then it's okay to ignore it, otherwise not.
@@ -6493,18 +6520,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6493
6520
update_committed = true;
6494
6521
update_xid = xid ;
6495
6522
}
6496
-
6497
- /*
6498
- * Not in progress, not committed -- must be aborted or crashed;
6499
- * we can ignore it.
6500
- */
6523
+ else
6524
+ {
6525
+ /*
6526
+ * Not in progress, not committed -- must be aborted or crashed;
6527
+ * we can ignore it.
6528
+ */
6529
+ }
6501
6530
6502
6531
/*
6503
6532
* Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6504
- * update Xid cannot possibly be older than the xid cutoff.
6533
+ * update Xid cannot possibly be older than the xid cutoff. The
6534
+ * presence of such a tuple would cause corruption, so be paranoid
6535
+ * and check.
6505
6536
*/
6506
- Assert (!TransactionIdIsValid (update_xid ) ||
6507
- !TransactionIdPrecedes (update_xid , cutoff_xid ));
6537
+ if (TransactionIdIsValid (update_xid ) &&
6538
+ TransactionIdPrecedes (update_xid , cutoff_xid ))
6539
+ ereport (ERROR ,
6540
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6541
+ errmsg_internal ("found update xid %u from before xid cutoff %u" ,
6542
+ update_xid , cutoff_xid )));
6508
6543
6509
6544
/*
6510
6545
* If we determined that it's an Xid corresponding to an update
@@ -6601,8 +6636,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6601
6636
* recovery. We really need to remove old xids.
6602
6637
*/
6603
6638
bool
6604
- heap_prepare_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
6605
- TransactionId cutoff_multi ,
6639
+ heap_prepare_freeze_tuple (HeapTupleHeader tuple ,
6640
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6641
+ TransactionId cutoff_xid , TransactionId cutoff_multi ,
6606
6642
xl_heap_freeze_tuple * frz , bool * totally_frozen_p )
6607
6643
{
6608
6644
bool changed = false;
@@ -6619,8 +6655,20 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6619
6655
xid = HeapTupleHeaderGetXmin (tuple );
6620
6656
if (TransactionIdIsNormal (xid ))
6621
6657
{
6658
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6659
+ ereport (ERROR ,
6660
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6661
+ errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
6662
+ xid , relfrozenxid )));
6663
+
6622
6664
if (TransactionIdPrecedes (xid , cutoff_xid ))
6623
6665
{
6666
+ if (!TransactionIdDidCommit (xid ))
6667
+ ereport (ERROR ,
6668
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6669
+ errmsg_internal ("uncommitted xmin %u from before xid cutoff %u needs to be frozen" ,
6670
+ xid , cutoff_xid )));
6671
+
6624
6672
frz -> t_infomask |= HEAP_XMIN_FROZEN ;
6625
6673
changed = true;
6626
6674
}
@@ -6645,6 +6693,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6645
6693
uint16 flags ;
6646
6694
6647
6695
newxmax = FreezeMultiXactId (xid , tuple -> t_infomask ,
6696
+ relfrozenxid , relminmxid ,
6648
6697
cutoff_xid , cutoff_multi , & flags );
6649
6698
6650
6699
if (flags & FRM_INVALIDATE_XMAX )
@@ -6694,8 +6743,28 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6694
6743
}
6695
6744
else if (TransactionIdIsNormal (xid ))
6696
6745
{
6746
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6747
+ ereport (ERROR ,
6748
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6749
+ errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
6750
+ xid , relfrozenxid )));
6751
+
6697
6752
if (TransactionIdPrecedes (xid , cutoff_xid ))
6753
+ {
6754
+ /*
6755
+ * If we freeze xmax, make absolutely sure that it's not an XID
6756
+ * that is important. (Note, a lock-only xmax can be removed
6757
+ * independent of committedness, since a committed lock holder has
6758
+ * released the lock).
6759
+ */
6760
+ if (!(tuple -> t_infomask & HEAP_XMAX_LOCK_ONLY ) &&
6761
+ TransactionIdDidCommit (xid ))
6762
+ ereport (ERROR ,
6763
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6764
+ errmsg_internal ("cannot freeze committed xmax %u" ,
6765
+ xid )));
6698
6766
freeze_xmax = true;
6767
+ }
6699
6768
else
6700
6769
totally_frozen = false;
6701
6770
}
@@ -6800,14 +6869,17 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
6800
6869
* Useful for callers like CLUSTER that perform their own WAL logging.
6801
6870
*/
6802
6871
bool
6803
- heap_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
6804
- TransactionId cutoff_multi )
6872
+ heap_freeze_tuple (HeapTupleHeader tuple ,
6873
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6874
+ TransactionId cutoff_xid , TransactionId cutoff_multi )
6805
6875
{
6806
6876
xl_heap_freeze_tuple frz ;
6807
6877
bool do_freeze ;
6808
6878
bool tuple_totally_frozen ;
6809
6879
6810
- do_freeze = heap_prepare_freeze_tuple (tuple , cutoff_xid , cutoff_multi ,
6880
+ do_freeze = heap_prepare_freeze_tuple (tuple ,
6881
+ relfrozenxid , relminmxid ,
6882
+ cutoff_xid , cutoff_multi ,
6811
6883
& frz , & tuple_totally_frozen );
6812
6884
6813
6885
/*
0 commit comments