Pavan Deolasee wrote: > I wonder if we should just avoid initialising those variables at the top > and take compiler's help to detect any missed branches. That way, we shall > know exactly why and where we are making them true/false. I also think that > we should differentiate between scenarios where xmin/xmax is already frozen > and scenarios where we are freezing them now.
After staring at this code for a while, it strikes me that the xmin case is different enough from the xmax case that it works better to let the logic be different; namely for xmin a single bool suffices. I kept your logic for xmax, except that xmax_already_frozen requires initialization (to 'false') or we risk having it end up as 'true' due to random garbage in the stack and set totally_frozen_p to true spuriously because of this (or spuriously fail the assertion in line 6942). I noticed this when running Dan's test case with your patch -- the assertion failed for the xmin case: TRAP: FailedAssertion(«!(!xmin_already_frozen)», Archivo: «/pgsql/source/master/src/backend/access/heap/heapam.c», Línea: 6845) That led me to the rewrite of the xmin logic, and it also led me to looking deeper at the xmax case (with which I didn't run into any assertion failure, but it's clear that it could definitely happen if the stack happens to have that bit set.) I also chose to accept the suggestion in XXX to throw an error: if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) ... else if (TransactionIdIsNormal(xid)) ... else if ((tuple->t_infomask & HEAP_XMAX_INVALID) || !TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple))) else ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg_internal("found xmax %u (infomask 0x%04x) not frozen, not multi, not normal", xid, tuple->t_infomask))); There's no place else in the backend where we report an infomask, but in this case it seems warranted (for forensics) if this elog ever fires. The tests involved are: which means that this ereport could fire is if the transaction is BootstrapTransactionId or FrozenTransactionId. In either case VACUUM should have removed the tuple as dead, so these cases shouldn't ever occur. (Looking at the other caller for this code, which is cluster.c for table rewrites, it appears that dead tuples are not passed to heap_freeze_tuple, so xmax=BootstrapXid/FrozenXid should not be a problem there either.) Patch attached. I intend to push this soon, to have it in next week's set. -- Álvaro Herrera https://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index 1a672150be..72395a50b8 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -6803,9 +6803,10 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz, bool *totally_frozen_p) { bool changed = false; - bool freeze_xmax = false; + bool xmax_already_frozen = false; + bool xmin_frozen; + bool freeze_xmax; TransactionId xid; - bool totally_frozen = true; frz->frzflags = 0; frz->t_infomask2 = tuple->t_infomask2; @@ -6814,6 +6815,8 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, /* Process xmin */ xid = HeapTupleHeaderGetXmin(tuple); + xmin_frozen = ((xid == FrozenTransactionId) || + HeapTupleHeaderXminFrozen(tuple)); if (TransactionIdIsNormal(xid)) { if (TransactionIdPrecedes(xid, relfrozenxid)) @@ -6832,9 +6835,8 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, frz->t_infomask |= HEAP_XMIN_FROZEN; changed = true; + xmin_frozen = true; } - else - totally_frozen = false; } /* @@ -6857,9 +6859,9 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, relfrozenxid, relminmxid, cutoff_xid, cutoff_multi, &flags); - if (flags & FRM_INVALIDATE_XMAX) - freeze_xmax = true; - else if (flags & FRM_RETURN_IS_XID) + freeze_xmax = (flags & FRM_INVALIDATE_XMAX); + + if (flags & FRM_RETURN_IS_XID) { /* * NB -- some of these transformations are only valid because we @@ -6873,7 +6875,6 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, if (flags & FRM_MARK_COMMITTED) frz->t_infomask |= HEAP_XMAX_COMMITTED; changed = true; - totally_frozen = false; } else if (flags & FRM_RETURN_IS_MULTI) { @@ -6895,11 +6896,6 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, frz->xmax = newxmax; changed = true; - totally_frozen = false; - } - else - { - Assert(flags & FRM_NOOP); } } else if (TransactionIdIsNormal(xid)) @@ -6927,11 +6923,24 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, freeze_xmax = true; } else - totally_frozen = false; + freeze_xmax = false; } + else if ((tuple->t_infomask & HEAP_XMAX_INVALID) || + !TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple))) + { + freeze_xmax = false; + xmax_already_frozen = true; + } + else + ereport(ERROR, + (errcode(ERRCODE_DATA_CORRUPTED), + errmsg_internal("found xmax %u (infomask 0x%04x) not frozen, not multi, not normal", + xid, tuple->t_infomask))); if (freeze_xmax) { + Assert(!xmax_already_frozen); + frz->xmax = InvalidTransactionId; /* @@ -6984,7 +6993,8 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple, } } - *totally_frozen_p = totally_frozen; + *totally_frozen_p = (xmin_frozen && + (freeze_xmax || xmax_already_frozen)); return changed; }