@@ -6030,6 +6030,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
6030
6030
*/
6031
6031
static TransactionId
6032
6032
FreezeMultiXactId (MultiXactId multi , uint16 t_infomask ,
6033
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6033
6034
TransactionId cutoff_xid , MultiXactId cutoff_multi ,
6034
6035
uint16 * flags )
6035
6036
{
@@ -6056,16 +6057,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6056
6057
* flags |= FRM_INVALIDATE_XMAX ;
6057
6058
return InvalidTransactionId ;
6058
6059
}
6060
+ else if (MultiXactIdPrecedes (multi , relminmxid ))
6061
+ ereport (ERROR ,
6062
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6063
+ errmsg_internal ("found multixact %u from before relminmxid %u" ,
6064
+ multi , relminmxid )));
6059
6065
else if (MultiXactIdPrecedes (multi , cutoff_multi ))
6060
6066
{
6061
6067
/*
6062
- * This old multi cannot possibly have members still running. If it
6063
- * was a locker only, it can be removed without any further
6064
- * consideration; but if it contained an update, we might need to
6065
- * preserve it.
6068
+ * This old multi cannot possibly have members still running, but
6069
+ * verify just in case. If it was a locker only, it can be removed
6070
+ * without any further consideration; but if it contained an update, we
6071
+ * might need to preserve it.
6066
6072
*/
6067
- Assert (!MultiXactIdIsRunning (multi ,
6068
- HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )));
6073
+ if (MultiXactIdIsRunning (multi ,
6074
+ HEAP_XMAX_IS_LOCKED_ONLY (t_infomask )))
6075
+ ereport (ERROR ,
6076
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6077
+ errmsg_internal ("multixact %u from before cutoff %u found to be still running" ,
6078
+ multi , cutoff_multi )));
6079
+
6069
6080
if (HEAP_XMAX_IS_LOCKED_ONLY (t_infomask ))
6070
6081
{
6071
6082
* flags |= FRM_INVALIDATE_XMAX ;
@@ -6079,13 +6090,22 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6079
6090
/* wasn't only a lock, xid needs to be valid */
6080
6091
Assert (TransactionIdIsValid (xid ));
6081
6092
6093
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6094
+ ereport (ERROR ,
6095
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6096
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6097
+ xid , relfrozenxid )));
6098
+
6082
6099
/*
6083
6100
* If the xid is older than the cutoff, it has to have aborted,
6084
6101
* otherwise the tuple would have gotten pruned away.
6085
6102
*/
6086
6103
if (TransactionIdPrecedes (xid , cutoff_xid ))
6087
6104
{
6088
- Assert (!TransactionIdDidCommit (xid ));
6105
+ if (TransactionIdDidCommit (xid ))
6106
+ ereport (ERROR ,
6107
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6108
+ errmsg_internal ("cannot freeze committed update xid %u" , xid )));
6089
6109
* flags |= FRM_INVALIDATE_XMAX ;
6090
6110
xid = InvalidTransactionId ; /* not strictly necessary */
6091
6111
}
@@ -6157,6 +6177,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6157
6177
{
6158
6178
TransactionId xid = members [i ].xid ;
6159
6179
6180
+ Assert (TransactionIdIsValid (xid ));
6181
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6182
+ ereport (ERROR ,
6183
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6184
+ errmsg_internal ("found update xid %u from before relfrozenxid %u" ,
6185
+ xid , relfrozenxid )));
6186
+
6160
6187
/*
6161
6188
* It's an update; should we keep it? If the transaction is known
6162
6189
* aborted or crashed then it's okay to ignore it, otherwise not.
@@ -6185,18 +6212,26 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6185
6212
update_committed = true;
6186
6213
update_xid = xid ;
6187
6214
}
6188
-
6189
- /*
6190
- * Not in progress, not committed -- must be aborted or crashed;
6191
- * we can ignore it.
6192
- */
6215
+ else
6216
+ {
6217
+ /*
6218
+ * Not in progress, not committed -- must be aborted or crashed;
6219
+ * we can ignore it.
6220
+ */
6221
+ }
6193
6222
6194
6223
/*
6195
6224
* Since the tuple wasn't marked HEAPTUPLE_DEAD by vacuum, the
6196
- * update Xid cannot possibly be older than the xid cutoff.
6225
+ * update Xid cannot possibly be older than the xid cutoff. The
6226
+ * presence of such a tuple would cause corruption, so be paranoid
6227
+ * and check.
6197
6228
*/
6198
- Assert (!TransactionIdIsValid (update_xid ) ||
6199
- !TransactionIdPrecedes (update_xid , cutoff_xid ));
6229
+ if (TransactionIdIsValid (update_xid ) &&
6230
+ TransactionIdPrecedes (update_xid , cutoff_xid ))
6231
+ ereport (ERROR ,
6232
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6233
+ errmsg_internal ("found update xid %u from before xid cutoff %u" ,
6234
+ update_xid , cutoff_xid )));
6200
6235
6201
6236
/*
6202
6237
* If we determined that it's an Xid corresponding to an update
@@ -6291,8 +6326,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
6291
6326
* recovery. We really need to remove old xids.
6292
6327
*/
6293
6328
bool
6294
- heap_prepare_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
6295
- TransactionId cutoff_multi ,
6329
+ heap_prepare_freeze_tuple (HeapTupleHeader tuple ,
6330
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6331
+ TransactionId cutoff_xid , TransactionId cutoff_multi ,
6296
6332
xl_heap_freeze_tuple * frz )
6297
6333
6298
6334
{
@@ -6307,11 +6343,25 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6307
6343
6308
6344
/* Process xmin */
6309
6345
xid = HeapTupleHeaderGetXmin (tuple );
6310
- if (TransactionIdIsNormal (xid ) &&
6311
- TransactionIdPrecedes (xid , cutoff_xid ))
6346
+ if (TransactionIdIsNormal (xid ))
6312
6347
{
6313
- frz -> t_infomask |= HEAP_XMIN_FROZEN ;
6314
- changed = true;
6348
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6349
+ ereport (ERROR ,
6350
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6351
+ errmsg_internal ("found xmin %u from before relfrozenxid %u" ,
6352
+ xid , relfrozenxid )));
6353
+
6354
+ if (TransactionIdPrecedes (xid , cutoff_xid ))
6355
+ {
6356
+ if (!TransactionIdDidCommit (xid ))
6357
+ ereport (ERROR ,
6358
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6359
+ errmsg_internal ("uncommitted xmin %u from before xid cutoff %u needs to be frozen" ,
6360
+ xid , cutoff_xid )));
6361
+
6362
+ frz -> t_infomask |= HEAP_XMIN_FROZEN ;
6363
+ changed = true;
6364
+ }
6315
6365
}
6316
6366
6317
6367
/*
@@ -6331,6 +6381,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6331
6381
uint16 flags ;
6332
6382
6333
6383
newxmax = FreezeMultiXactId (xid , tuple -> t_infomask ,
6384
+ relfrozenxid , relminmxid ,
6334
6385
cutoff_xid , cutoff_multi , & flags );
6335
6386
6336
6387
if (flags & FRM_INVALIDATE_XMAX )
@@ -6376,10 +6427,30 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, TransactionId cutoff_xid,
6376
6427
Assert (flags & FRM_NOOP );
6377
6428
}
6378
6429
}
6379
- else if (TransactionIdIsNormal (xid ) &&
6380
- TransactionIdPrecedes (xid , cutoff_xid ))
6430
+ else if (TransactionIdIsNormal (xid ))
6381
6431
{
6382
- freeze_xmax = true;
6432
+ if (TransactionIdPrecedes (xid , relfrozenxid ))
6433
+ ereport (ERROR ,
6434
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6435
+ errmsg_internal ("found xmax %u from before relfrozenxid %u" ,
6436
+ xid , relfrozenxid )));
6437
+
6438
+ if (TransactionIdPrecedes (xid , cutoff_xid ))
6439
+ {
6440
+ /*
6441
+ * If we freeze xmax, make absolutely sure that it's not an XID
6442
+ * that is important. (Note, a lock-only xmax can be removed
6443
+ * independent of committedness, since a committed lock holder has
6444
+ * released the lock).
6445
+ */
6446
+ if (!(tuple -> t_infomask & HEAP_XMAX_LOCK_ONLY ) &&
6447
+ TransactionIdDidCommit (xid ))
6448
+ ereport (ERROR ,
6449
+ (errcode (ERRCODE_DATA_CORRUPTED ),
6450
+ errmsg_internal ("cannot freeze committed xmax %u" ,
6451
+ xid )));
6452
+ freeze_xmax = true;
6453
+ }
6383
6454
}
6384
6455
6385
6456
if (freeze_xmax )
@@ -6473,13 +6544,16 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
6473
6544
* Useful for callers like CLUSTER that perform their own WAL logging.
6474
6545
*/
6475
6546
bool
6476
- heap_freeze_tuple (HeapTupleHeader tuple , TransactionId cutoff_xid ,
6477
- TransactionId cutoff_multi )
6547
+ heap_freeze_tuple (HeapTupleHeader tuple ,
6548
+ TransactionId relfrozenxid , TransactionId relminmxid ,
6549
+ TransactionId cutoff_xid , TransactionId cutoff_multi )
6478
6550
{
6479
6551
xl_heap_freeze_tuple frz ;
6480
6552
bool do_freeze ;
6481
6553
6482
- do_freeze = heap_prepare_freeze_tuple (tuple , cutoff_xid , cutoff_multi ,
6554
+ do_freeze = heap_prepare_freeze_tuple (tuple ,
6555
+ relfrozenxid , relminmxid ,
6556
+ cutoff_xid , cutoff_multi ,
6483
6557
& frz );
6484
6558
6485
6559
/*
0 commit comments