@ -6098,9 +6098,7 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
* MultiXactId .
* MultiXactId .
*
*
* " flags " is an output value ; it ' s used to tell caller what to do on return .
* " flags " is an output value ; it ' s used to tell caller what to do on return .
*
* " pagefrz " is an input / output value , used to manage page level freezing .
* " mxid_oldest_xid_out " is an output value ; it ' s used to track the oldest
* extant Xid within any Multixact that will remain after freezing executes .
*
*
* Possible values that we can set in " flags " :
* Possible values that we can set in " flags " :
* FRM_NOOP
* FRM_NOOP
@ -6115,17 +6113,37 @@ heap_inplace_update(Relation relation, HeapTuple tuple)
* The return value is a new MultiXactId to set as new Xmax .
* The return value is a new MultiXactId to set as new Xmax .
* ( caller must obtain proper infomask bits using GetMultiXactIdHintBits )
* ( caller must obtain proper infomask bits using GetMultiXactIdHintBits )
*
*
* " mxid_oldest_xid_out " is only set when " flags " contains either FRM_NOOP or
* Caller delegates control of page freezing to us . In practice we always
* FRM_RETURN_IS_MULTI , since we only leave behind a MultiXactId for these .
* force freezing of caller ' s page unless FRM_NOOP processing is indicated .
*
* We help caller ensure that XIDs < FreezeLimit and MXIDs < MultiXactCutoff
* NB : Creates a _new_ MultiXactId when FRM_RETURN_IS_MULTI is set in " flags " .
* can never be left behind . We freely choose when and how to process each
* Multi , without ever violating the cutoff postconditions for freezing .
*
* It ' s useful to remove Multis on a proactive timeline ( relative to freezing
* XIDs ) to keep MultiXact member SLRU buffer misses to a minimum . It can also
* be cheaper in the short run , for us , since we too can avoid SLRU buffer
* misses through eager processing .
*
* NB : Creates a _new_ MultiXactId when FRM_RETURN_IS_MULTI is set , though only
* when FreezeLimit and / or MultiXactCutoff cutoffs leave us with no choice .
* This can usually be put off , which is usually enough to avoid it altogether .
* Allocating new multis during VACUUM should be avoided on general principle ;
* only VACUUM can advance relminmxid , so allocating new Multis here comes with
* its own special risks .
*
* NB : Caller must maintain " no freeze " NewRelfrozenXid / NewRelminMxid trackers
* using heap_tuple_should_freeze when we haven ' t forced page - level freezing .
*
* NB : Caller should avoid needlessly calling heap_tuple_should_freeze when we
* have already forced page - level freezing , since that might incur the same
* SLRU buffer misses that we specifically intended to avoid by freezing .
*/
*/
static TransactionId
static TransactionId
FreezeMultiXactId ( MultiXactId multi , uint16 t_infomask ,
FreezeMultiXactId ( MultiXactId multi , uint16 t_infomask ,
const struct VacuumCutoffs * cutoffs , uint16 * flags ,
const struct VacuumCutoffs * cutoffs , uint16 * flags ,
TransactionId * mxid_oldest_xid_out )
HeapPageFreeze * pagefrz )
{
{
TransactionId newxmax = InvalidTransactionId ;
TransactionId newxmax ;
MultiXactMember * members ;
MultiXactMember * members ;
int nmembers ;
int nmembers ;
bool need_replace ;
bool need_replace ;
@ -6134,7 +6152,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
bool has_lockers ;
bool has_lockers ;
TransactionId update_xid ;
TransactionId update_xid ;
bool update_committed ;
bool update_committed ;
TransactionId temp_xid_out ;
TransactionId FreezePageRelfrozenXid ;
* flags = 0 ;
* flags = 0 ;
@ -6144,8 +6162,8 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
if ( ! MultiXactIdIsValid ( multi ) | |
if ( ! MultiXactIdIsValid ( multi ) | |
HEAP_LOCKED_UPGRADED ( t_infomask ) )
HEAP_LOCKED_UPGRADED ( t_infomask ) )
{
{
/* Ensure infomask bits are appropriately set/reset */
* flags | = FRM_INVALIDATE_XMAX ;
* flags | = FRM_INVALIDATE_XMAX ;
pagefrz - > freeze_required = true ;
return InvalidTransactionId ;
return InvalidTransactionId ;
}
}
else if ( MultiXactIdPrecedes ( multi , cutoffs - > relminmxid ) )
else if ( MultiXactIdPrecedes ( multi , cutoffs - > relminmxid ) )
@ -6153,8 +6171,10 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " found multixact %u from before relminmxid %u " ,
errmsg_internal ( " found multixact %u from before relminmxid %u " ,
multi , cutoffs - > relminmxid ) ) ) ;
multi , cutoffs - > relminmxid ) ) ) ;
else if ( MultiXactIdPrecedes ( multi , cutoffs - > MultiXactCutoff ) )
else if ( MultiXactIdPrecedes ( multi , cutoffs - > OldestMxact ) )
{
{
TransactionId update_xact ;
/*
/*
* This old multi cannot possibly have members still running , but
* This old multi cannot possibly have members still running , but
* verify just in case . If it was a locker only , it can be removed
* verify just in case . If it was a locker only , it can be removed
@ -6165,52 +6185,46 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
HEAP_XMAX_IS_LOCKED_ONLY ( t_infomask ) ) )
HEAP_XMAX_IS_LOCKED_ONLY ( t_infomask ) ) )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " multixact %u from before cutoff %u found to be still running " ,
errmsg_internal ( " multixact %u from before multi freeze cutoff %u found to be still running " ,
multi , cutoffs - > MultiXactCutoff ) ) ) ;
multi , cutoffs - > OldestMxact ) ) ) ;
if ( HEAP_XMAX_IS_LOCKED_ONLY ( t_infomask ) )
if ( HEAP_XMAX_IS_LOCKED_ONLY ( t_infomask ) )
{
{
* flags | = FRM_INVALIDATE_XMAX ;
* flags | = FRM_INVALIDATE_XMAX ;
newxmax = InvalidTransactionId ;
pagefrz - > freeze_required = true ;
return InvalidTransactionId ;
}
}
else
{
/* replace multi with single XID for its updater */
newxmax = MultiXactIdGetUpdateXid ( multi , t_infomask ) ;
/* wasn't only a lock, xid needs to be valid */
Assert ( TransactionIdIsValid ( newxmax ) ) ;
if ( TransactionIdPrecedes ( newxmax , cutoffs - > relfrozenxid ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " found update xid %u from before relfrozenxid %u " ,
newxmax , cutoffs - > relfrozenxid ) ) ) ;
/* replace multi with single XID for its updater? */
update_xact = MultiXactIdGetUpdateXid ( multi , t_infomask ) ;
if ( TransactionIdPrecedes ( update_xact , cutoffs - > relfrozenxid ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " multixact %u contains update XID %u from before relfrozenxid %u " ,
multi , update_xact ,
cutoffs - > relfrozenxid ) ) ) ;
else if ( TransactionIdPrecedes ( update_xact , cutoffs - > OldestXmin ) )
{
/*
/*
* If the new xmax xid is older than OldestXmin , it has to have
* Updater XID has to have aborted ( otherwise the tuple would have
* aborted , otherwise the tuple would have been pruned away
* been pruned away instead , since updater XID is < OldestXmin ) .
* Just remove xmax .
*/
*/
if ( TransactionIdPrecedes ( newxmax , cutoffs - > OldestXmin ) )
if ( TransactionIdDidCommit ( update_xact ) )
{
ereport ( ERROR ,
if ( TransactionIdDidCommit ( newxmax ) )
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
ereport ( ERROR ,
errmsg_internal ( " multixact %u contains committed update XID %u from before removable cutoff %u " ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
multi , update_xact ,
errmsg_internal ( " cannot freeze committed update xid %u " , newxmax ) ) ) ;
cutoffs - > OldestXmin ) ) ) ;
* flags | = FRM_INVALIDATE_XMAX ;
* flags | = FRM_INVALIDATE_XMAX ;
newxmax = InvalidTransactionId ;
pagefrz - > freeze_required = true ;
}
return InvalidTransactionId ;
else
{
* flags | = FRM_RETURN_IS_XID ;
}
}
}
/*
/* Have to keep updater XID as new xmax */
* Don ' t push back mxid_oldest_xid_out using FRM_RETURN_IS_XID Xid , or
* flags | = FRM_RETURN_IS_XID ;
* when no Xids will remain
pagefrz - > freeze_required = true ;
*/
return update_xact ;
return newxmax ;
}
}
/*
/*
@ -6225,11 +6239,30 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
{
{
/* Nothing worth keeping */
/* Nothing worth keeping */
* flags | = FRM_INVALIDATE_XMAX ;
* flags | = FRM_INVALIDATE_XMAX ;
pagefrz - > freeze_required = true ;
return InvalidTransactionId ;
return InvalidTransactionId ;
}
}
/*
* The FRM_NOOP case is the only case where we might need to ratchet back
* FreezePageRelfrozenXid or FreezePageRelminMxid . It is also the only
* case where our caller might ratchet back its NoFreezePageRelfrozenXid
* or NoFreezePageRelminMxid " no freeze " trackers to deal with a multi .
* FRM_NOOP handling should result in the NewRelfrozenXid / NewRelminMxid
* trackers managed by VACUUM being ratcheting back by xmax to the degree
* required to make it safe to leave xmax undisturbed , independent of
* whether or not page freezing is triggered somewhere else .
*
* Our policy is to force freezing in every case other than FRM_NOOP ,
* which obviates the need to maintain either set of trackers , anywhere .
* Every other case will reliably execute a freeze plan for xmax that
* either replaces xmax with an XID / MXID > = OldestXmin / OldestMxact , or
* sets xmax to an InvalidTransactionId XID , rendering xmax fully frozen .
* ( VACUUM ' s NewRelfrozenXid / NewRelminMxid trackers are initialized with
* OldestXmin / OldestMxact , so later values never need to be tracked here . )
*/
need_replace = false ;
need_replace = false ;
temp_xid_out = * mxid_oldest_xid_out ; /* init for FRM_NOOP */
FreezePageRelfrozenXid = pagefrz - > FreezePageRelfrozenXid ;
for ( int i = 0 ; i < nmembers ; i + + )
for ( int i = 0 ; i < nmembers ; i + + )
{
{
TransactionId xid = members [ i ] . xid ;
TransactionId xid = members [ i ] . xid ;
@ -6238,26 +6271,29 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
{
{
/* Can't violate the FreezeLimit postcondition */
need_replace = true ;
need_replace = true ;
break ;
break ;
}
}
if ( TransactionIdPrecedes ( members [ i ] . xid , temp_xid_out ) )
if ( TransactionIdPrecedes ( xid , FreezePageRelfrozenXid ) )
temp_xid_out = members [ i ] . xid ;
FreezePageRelfrozenXid = xid ;
}
}
/*
/* Can't violate the MultiXactCutoff postcondition, either */
* In the simplest case , there is no member older than FreezeLimit ; we can
if ( ! need_replace )
* keep the existing MultiXactId as - is , avoiding a more expensive second
need_replace = MultiXactIdPrecedes ( multi , cutoffs - > MultiXactCutoff ) ;
* pass over the multi
*/
if ( ! need_replace )
if ( ! need_replace )
{
{
/*
/*
* When mxid_oldest_xid_out gets pushed back here it ' s likely that the
* vacuumlazy . c might ratchet back NewRelminMxid , NewRelfrozenXid , or
* update Xid was the oldest member , but we don ' t rely on that
* both together to make it safe to retain this particular multi after
* freezing its page
*/
*/
* flags | = FRM_NOOP ;
* flags | = FRM_NOOP ;
* mxid_oldest_xid_out = temp_xid_out ;
pagefrz - > FreezePageRelfrozenXid = FreezePageRelfrozenXid ;
if ( MultiXactIdPrecedes ( multi , pagefrz - > FreezePageRelminMxid ) )
pagefrz - > FreezePageRelminMxid = multi ;
pfree ( members ) ;
pfree ( members ) ;
return multi ;
return multi ;
}
}
@ -6266,13 +6302,15 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
* Do a more thorough second pass over the multi to figure out which
* Do a more thorough second pass over the multi to figure out which
* member XIDs actually need to be kept . Checking the precise status of
* member XIDs actually need to be kept . Checking the precise status of
* individual members might even show that we don ' t need to keep anything .
* individual members might even show that we don ' t need to keep anything .
* That is quite possible even though the Multi must be > = OldestMxact ,
* since our second pass only keeps member XIDs when it ' s truly necessary ;
* even member XIDs > = OldestXmin often won ' t be kept by second pass .
*/
*/
nnewmembers = 0 ;
nnewmembers = 0 ;
newmembers = palloc ( sizeof ( MultiXactMember ) * nmembers ) ;
newmembers = palloc ( sizeof ( MultiXactMember ) * nmembers ) ;
has_lockers = false ;
has_lockers = false ;
update_xid = InvalidTransactionId ;
update_xid = InvalidTransactionId ;
update_committed = false ;
update_committed = false ;
temp_xid_out = * mxid_oldest_xid_out ; /* init for FRM_RETURN_IS_MULTI */
/*
/*
* Determine whether to keep each member xid , or to ignore it instead
* Determine whether to keep each member xid , or to ignore it instead
@ -6293,14 +6331,14 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
if ( TransactionIdIsCurrentTransactionId ( xid ) | |
if ( TransactionIdIsCurrentTransactionId ( xid ) | |
TransactionIdIsInProgress ( xid ) )
TransactionIdIsInProgress ( xid ) )
{
{
if ( TransactionIdPrecedes ( xid , cutoffs - > OldestXmin ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " multixact %u contains running locker XID %u from before removable cutoff %u " ,
multi , xid ,
cutoffs - > OldestXmin ) ) ) ;
newmembers [ nnewmembers + + ] = members [ i ] ;
newmembers [ nnewmembers + + ] = members [ i ] ;
has_lockers = true ;
has_lockers = true ;
/*
* Cannot possibly be older than VACUUM ' s OldestXmin , so we
* don ' t need a NewRelfrozenXid step here
*/
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > OldestXmin , xid ) ) ;
}
}
continue ;
continue ;
@ -6310,15 +6348,13 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
* Updater XID ( not locker XID ) . Should we keep it ?
* Updater XID ( not locker XID ) . Should we keep it ?
*
*
* Since the tuple wasn ' t totally removed when vacuum pruned , the
* Since the tuple wasn ' t totally removed when vacuum pruned , the
* update Xid cannot possibly be older than OldestXmin cutoff . The
* update Xid cannot possibly be older than OldestXmin cutoff unless
* presence of such a tuple would cause corruption , so be paranoid and
* the updater XID aborted . If the updater transaction is known
* check .
* aborted or crashed then it ' s okay to ignore it , otherwise not .
*
* In any case the Multi should never contain two updaters , whatever
* their individual commit status . Check for that first , in passing .
*/
*/
if ( TransactionIdPrecedes ( xid , cutoffs - > OldestXmin ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " found update xid %u from before removable cutoff %u " ,
xid , cutoffs - > OldestXmin ) ) ) ;
if ( TransactionIdIsValid ( update_xid ) )
if ( TransactionIdIsValid ( update_xid ) )
ereport ( ERROR ,
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
@ -6328,9 +6364,6 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
update_xid , xid ) ) ) ;
update_xid , xid ) ) ) ;
/*
/*
* If the transaction is known aborted or crashed then it ' s okay to
* ignore it , otherwise not .
*
* As with all tuple visibility routines , it ' s critical to test
* As with all tuple visibility routines , it ' s critical to test
* TransactionIdIsInProgress before TransactionIdDidCommit , because of
* TransactionIdIsInProgress before TransactionIdDidCommit , because of
* race conditions explained in detail in heapam_visibility . c .
* race conditions explained in detail in heapam_visibility . c .
@ -6358,13 +6391,15 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
}
}
/*
/*
* We determined that this is an Xid corresponding to an update that
* We determined that updater must be kept - - add it to pending new
* must be retained - - add it to new members list for later . Also
* members list
* consider pushing back mxid_oldest_xid_out .
*/
*/
if ( TransactionIdPrecedes ( xid , cutoffs - > OldestXmin ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " multixact %u contains committed update XID %u from before removable cutoff %u " ,
multi , xid , cutoffs - > OldestXmin ) ) ) ;
newmembers [ nnewmembers + + ] = members [ i ] ;
newmembers [ nnewmembers + + ] = members [ i ] ;
if ( TransactionIdPrecedes ( xid , temp_xid_out ) )
temp_xid_out = xid ;
}
}
pfree ( members ) ;
pfree ( members ) ;
@ -6375,10 +6410,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
*/
*/
if ( nnewmembers = = 0 )
if ( nnewmembers = = 0 )
{
{
/* nothing worth keeping!? Tell caller to remove the whole th ing */
/* Nothing worth keep ing */
* flags | = FRM_INVALIDATE_XMAX ;
* flags | = FRM_INVALIDATE_XMAX ;
newxmax = InvalidTransactionId ;
newxmax = InvalidTransactionId ;
/* Don't push back mxid_oldest_xid_out -- no Xids will remain */
}
}
else if ( TransactionIdIsValid ( update_xid ) & & ! has_lockers )
else if ( TransactionIdIsValid ( update_xid ) & & ! has_lockers )
{
{
@ -6394,22 +6428,20 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
if ( update_committed )
if ( update_committed )
* flags | = FRM_MARK_COMMITTED ;
* flags | = FRM_MARK_COMMITTED ;
newxmax = update_xid ;
newxmax = update_xid ;
/* Don't push back mxid_oldest_xid_out using FRM_RETURN_IS_XID Xid */
}
}
else
else
{
{
/*
/*
* Create a new multixact with the surviving members of the previous
* Create a new multixact with the surviving members of the previous
* one , to set as new Xmax in the tuple . The oldest surviving member
* one , to set as new Xmax in the tuple
* might push back mxid_oldest_xid_out .
*/
*/
newxmax = MultiXactIdCreateFromMembers ( nnewmembers , newmembers ) ;
newxmax = MultiXactIdCreateFromMembers ( nnewmembers , newmembers ) ;
* flags | = FRM_RETURN_IS_MULTI ;
* flags | = FRM_RETURN_IS_MULTI ;
* mxid_oldest_xid_out = temp_xid_out ;
}
}
pfree ( newmembers ) ;
pfree ( newmembers ) ;
pagefrz - > freeze_required = true ;
return newxmax ;
return newxmax ;
}
}
@ -6417,9 +6449,9 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
* heap_prepare_freeze_tuple
* heap_prepare_freeze_tuple
*
*
* Check to see whether any of the XID fields of a tuple ( xmin , xmax , xvac )
* Check to see whether any of the XID fields of a tuple ( xmin , xmax , xvac )
* are older than the FreezeLimit and / or MultiXactCutoff freeze cutoffs . If so ,
* are older than the OldestXmin and / or OldestMxact freeze cutoffs . If so ,
* setup enough state ( in the * frz output argument ) to later execute and
* setup enough state ( in the * frz output argument ) to enable caller to
* WAL - log what caller needs to do for the tupl e, and return true . Return
* process this tuple as part of freezing its pag e, and return true . Return
* false if nothing can be changed about the tuple right now .
* false if nothing can be changed about the tuple right now .
*
*
* Also sets * totally_frozen to true if the tuple will be totally frozen once
* Also sets * totally_frozen to true if the tuple will be totally frozen once
@ -6427,22 +6459,30 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
* frozen by an earlier VACUUM ) . This indicates that there are no remaining
* frozen by an earlier VACUUM ) . This indicates that there are no remaining
* XIDs or MultiXactIds that will need to be processed by a future VACUUM .
* XIDs or MultiXactIds that will need to be processed by a future VACUUM .
*
*
* VACUUM caller must assemble HeapTupleFreeze entries for every tuple that we
* VACUUM caller must assemble HeapTupleFreeze freeze plan entries for every
* returned true for when called . A later heap_freeze_execute_prepared call
* tuple that we returned true for , and call heap_freeze_execute_prepared to
* will execute freezing for caller ' s page as a whole .
* execute freezing . Caller must initialize pagefrz fields for page as a
* whole before first call here for each heap page .
*
* VACUUM caller decides on whether or not to freeze the page as a whole .
* We ' ll often prepare freeze plans for a page that caller just discards .
* However , VACUUM doesn ' t always get to make a choice ; it must freeze when
* pagefrz . freeze_required is set , to ensure that any XIDs < FreezeLimit ( and
* MXIDs < MultiXactCutoff ) can never be left behind . We help to make sure
* that VACUUM always follows that rule .
*
* We sometimes force freezing of xmax MultiXactId values long before it is
* strictly necessary to do so just to ensure the FreezeLimit postcondition .
* It ' s worth processing MultiXactIds proactively when it is cheap to do so ,
* and it ' s convenient to make that happen by piggy - backing it on the " force
* freezing " mechanism. Conversely, we sometimes delay freezing MultiXactIds
* because it is expensive right now ( though only when it ' s still possible to
* do so without violating the FreezeLimit / MultiXactCutoff postcondition ) .
*
*
* It is assumed that the caller has checked the tuple with
* It is assumed that the caller has checked the tuple with
* HeapTupleSatisfiesVacuum ( ) and determined that it is not HEAPTUPLE_DEAD
* HeapTupleSatisfiesVacuum ( ) and determined that it is not HEAPTUPLE_DEAD
* ( else we should be removing the tuple , not freezing it ) .
* ( else we should be removing the tuple , not freezing it ) .
*
*
* The * relfrozenxid_out and * relminmxid_out arguments are the current target
* relfrozenxid and relminmxid for VACUUM caller ' s heap rel . Any and all
* unfrozen XIDs or MXIDs that remain in caller ' s rel after VACUUM finishes
* _must_ have values > = the final relfrozenxid / relminmxid values in pg_class .
* This includes XIDs that remain as MultiXact members from any tuple ' s xmax .
* Each call here pushes back * relfrozenxid_out and / or * relminmxid_out as
* needed to avoid unsafe final values in rel ' s authoritative pg_class tuple .
*
* NB : This function has side effects : it might allocate a new MultiXactId .
* NB : This function has side effects : it might allocate a new MultiXactId .
* It will be set as tuple ' s new xmax when our * frz output is processed within
* It will be set as tuple ' s new xmax when our * frz output is processed within
* heap_execute_freeze_tuple later on . If the tuple is in a shared buffer
* heap_execute_freeze_tuple later on . If the tuple is in a shared buffer
@ -6451,9 +6491,8 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
bool
bool
heap_prepare_freeze_tuple ( HeapTupleHeader tuple ,
heap_prepare_freeze_tuple ( HeapTupleHeader tuple ,
const struct VacuumCutoffs * cutoffs ,
const struct VacuumCutoffs * cutoffs ,
HeapTupleFreeze * frz , bool * totally_frozen ,
HeapPageFreeze * pagefrz ,
TransactionId * relfrozenxid_out ,
HeapTupleFreeze * frz , bool * totally_frozen )
MultiXactId * relminmxid_out )
{
{
bool xmin_already_frozen = false ,
bool xmin_already_frozen = false ,
xmax_already_frozen = false ;
xmax_already_frozen = false ;
@ -6470,7 +6509,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
/*
/*
* Process xmin , while keeping track of whether it ' s already frozen , or
* Process xmin , while keeping track of whether it ' s already frozen , or
* will become frozen when our freeze plan is executed by caller ( could be
* will become frozen iff our freeze plan is executed by caller ( could be
* neither ) .
* neither ) .
*/
*/
xid = HeapTupleHeaderGetXmin ( tuple ) ;
xid = HeapTupleHeaderGetXmin ( tuple ) ;
@ -6484,21 +6523,14 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
errmsg_internal ( " found xmin %u from before relfrozenxid %u " ,
errmsg_internal ( " found xmin %u from before relfrozenxid %u " ,
xid , cutoffs - > relfrozenxid ) ) ) ;
xid , cutoffs - > relfrozenxid ) ) ) ;
freeze_xmin = TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) ;
freeze_xmin = TransactionIdPrecedes ( xid , cutoffs - > OldestXmin ) ;
if ( freeze_xmin )
if ( freeze_xmin & & ! TransactionIdDidCommit ( xid ) )
{
ereport ( ERROR ,
if ( ! TransactionIdDidCommit ( xid ) )
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
ereport ( ERROR ,
errmsg_internal ( " uncommitted xmin %u from before xid cutoff %u needs to be frozen " ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
xid , cutoffs - > OldestXmin ) ) ) ;
errmsg_internal ( " uncommitted xmin %u from before xid cutoff %u needs to be frozen " ,
xid , cutoffs - > FreezeLimit ) ) ) ;
/* Will set freeze_xmin flags in freeze plan below */
}
else
{
/* xmin to remain unfrozen. Could push back relfrozenxid_out. */
if ( TransactionIdPrecedes ( xid , * relfrozenxid_out ) )
* relfrozenxid_out = xid ;
}
}
}
/*
/*
@ -6515,41 +6547,59 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
* For Xvac , we always freeze proactively . This allows totally_frozen
* For Xvac , we always freeze proactively . This allows totally_frozen
* tracking to ignore xvac .
* tracking to ignore xvac .
*/
*/
replace_xvac = true ;
replace_xvac = pagefrz - > freeze_required = true ;
/* Will set replace_xvac flags in freeze plan below */
}
}
/*
/* Now process xmax */
* Process xmax . To thoroughly examine the current Xmax value we need to
* resolve a MultiXactId to its member Xids , in case some of them are
* below the given FreezeLimit . In that case , those values might need
* freezing , too . Also , if a multi needs freezing , we cannot simply take
* it out - - - if there ' s a live updater Xid , it needs to be kept .
*
* Make sure to keep heap_tuple_would_freeze in sync with this .
*/
xid = HeapTupleHeaderGetRawXmax ( tuple ) ;
xid = HeapTupleHeaderGetRawXmax ( tuple ) ;
if ( tuple - > t_infomask & HEAP_XMAX_IS_MULTI )
if ( tuple - > t_infomask & HEAP_XMAX_IS_MULTI )
{
{
/* Raw xmax is a MultiXactId */
/* Raw xmax is a MultiXactId */
TransactionId newxmax ;
TransactionId newxmax ;
uint16 flags ;
uint16 flags ;
TransactionId mxid_oldest_xid_out = * relfrozenxid_out ;
/*
* We will either remove xmax completely ( in the " freeze_xmax " path ) ,
* process xmax by replacing it ( in the " replace_xmax " path ) , or
* perform no - op xmax processing . The only constraint is that the
* FreezeLimit / MultiXactCutoff postcondition must never be violated .
*/
newxmax = FreezeMultiXactId ( xid , tuple - > t_infomask , cutoffs ,
newxmax = FreezeMultiXactId ( xid , tuple - > t_infomask , cutoffs ,
& flags , & mxid_oldest_xid_out ) ;
& flags , pagefrz ) ;
if ( flags & FRM_RETURN_IS_XID )
if ( flags & FRM_NOOP )
{
/*
* xmax is a MultiXactId , and nothing about it changes for now .
* This is the only case where ' freeze_required ' won ' t have been
* set for us by FreezeMultiXactId , as well as the only case where
* neither freeze_xmax nor replace_xmax are set ( given a multi ) .
*
* This is a no - op , but the call to FreezeMultiXactId might have
* ratcheted back NewRelfrozenXid and / or NewRelminMxid trackers
* for us ( the " freeze page " variants , specifically ) . That ' ll
* make it safe for our caller to freeze the page later on , while
* leaving this particular xmax undisturbed .
*
* FreezeMultiXactId is _not_ responsible for the " no freeze "
* NewRelfrozenXid / NewRelminMxid trackers , though - - that ' s our
* job . A call to heap_tuple_should_freeze for this same tuple
* will take place below if ' freeze_required ' isn ' t set already .
* ( This repeats work from FreezeMultiXactId , but allows " no
* freeze " tracker maintenance to happen in only one place.)
*/
Assert ( ! MultiXactIdPrecedes ( newxmax , cutoffs - > MultiXactCutoff ) ) ;
Assert ( MultiXactIdIsValid ( newxmax ) & & xid = = newxmax ) ;
}
else if ( flags & FRM_RETURN_IS_XID )
{
{
/*
/*
* xmax will become an updater Xid ( original MultiXact ' s updater
* xmax will become an updater Xid ( original MultiXact ' s updater
* member Xid will be carried forward as a simple Xid in Xmax ) .
* member Xid will be carried forward as a simple Xid in Xmax ) .
* Might have to ratchet back relfrozenxid_out here , though never
* relminmxid_out .
*/
*/
Assert ( ! TransactionIdPrecedes ( newxmax , cutoffs - > OldestXmin ) ) ;
Assert ( ! TransactionIdPrecedes ( newxmax , cutoffs - > OldestXmin ) ) ;
if ( TransactionIdPrecedes ( newxmax , * relfrozenxid_out ) )
* relfrozenxid_out = newxmax ;
/*
/*
* NB - - some of these transformations are only valid because we
* NB - - some of these transformations are only valid because we
@ -6572,13 +6622,8 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
/*
/*
* xmax is an old MultiXactId that we have to replace with a new
* xmax is an old MultiXactId that we have to replace with a new
* MultiXactId , to carry forward two or more original member XIDs .
* MultiXactId , to carry forward two or more original member XIDs .
* Might have to ratchet back relfrozenxid_out here , though never
* relminmxid_out .
*/
*/
Assert ( ! MultiXactIdPrecedes ( newxmax , cutoffs - > OldestMxact ) ) ;
Assert ( ! MultiXactIdPrecedes ( newxmax , cutoffs - > OldestMxact ) ) ;
Assert ( TransactionIdPrecedesOrEquals ( mxid_oldest_xid_out ,
* relfrozenxid_out ) ) ;
* relfrozenxid_out = mxid_oldest_xid_out ;
/*
/*
* We can ' t use GetMultiXactIdHintBits directly on the new multi
* We can ' t use GetMultiXactIdHintBits directly on the new multi
@ -6594,20 +6639,6 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
frz - > xmax = newxmax ;
frz - > xmax = newxmax ;
replace_xmax = true ;
replace_xmax = true ;
}
}
else if ( flags & FRM_NOOP )
{
/*
* xmax is a MultiXactId , and nothing about it changes for now .
* Might have to ratchet back relminmxid_out , relfrozenxid_out , or
* both together .
*/
Assert ( MultiXactIdIsValid ( newxmax ) & & xid = = newxmax ) ;
Assert ( TransactionIdPrecedesOrEquals ( mxid_oldest_xid_out ,
* relfrozenxid_out ) ) ;
if ( MultiXactIdPrecedes ( xid , * relminmxid_out ) )
* relminmxid_out = xid ;
* relfrozenxid_out = mxid_oldest_xid_out ;
}
else
else
{
{
/*
/*
@ -6617,9 +6648,12 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
Assert ( flags & FRM_INVALIDATE_XMAX ) ;
Assert ( flags & FRM_INVALIDATE_XMAX ) ;
Assert ( ! TransactionIdIsValid ( newxmax ) ) ;
Assert ( ! TransactionIdIsValid ( newxmax ) ) ;
/* Will set t_infomask/t_infomask2 flags in freeze plan below */
/* Will set freeze_xmax flags in freeze plan below */
freeze_xmax = true ;
freeze_xmax = true ;
}
}
/* MultiXactId processing forces freezing (barring FRM_NOOP case) */
Assert ( pagefrz - > freeze_required | | ( ! freeze_xmax & & ! replace_xmax ) ) ;
}
}
else if ( TransactionIdIsNormal ( xid ) )
else if ( TransactionIdIsNormal ( xid ) )
{
{
@ -6630,28 +6664,21 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
errmsg_internal ( " found xmax %u from before relfrozenxid %u " ,
errmsg_internal ( " found xmax %u from before relfrozenxid %u " ,
xid , cutoffs - > relfrozenxid ) ) ) ;
xid , cutoffs - > relfrozenxid ) ) ) ;
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
if ( TransactionIdPrecedes ( xid , cutoffs - > OldestXmin ) )
{
/*
* If we freeze xmax , make absolutely sure that it ' s not an XID
* that is important . ( Note , a lock - only xmax can be removed
* independent of committedness , since a committed lock holder has
* released the lock ) .
*/
if ( ! HEAP_XMAX_IS_LOCKED_ONLY ( tuple - > t_infomask ) & &
TransactionIdDidCommit ( xid ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " cannot freeze committed xmax %u " ,
xid ) ) ) ;
freeze_xmax = true ;
freeze_xmax = true ;
/* No need for relfrozenxid_out handling, since we'll freeze xmax */
}
/*
else
* If we freeze xmax , make absolutely sure that it ' s not an XID that
{
* is important . ( Note , a lock - only xmax can be removed independent
if ( TransactionIdPrecedes ( xid , * relfrozenxid_out ) )
* of committedness , since a committed lock holder has released the
* relfrozenxid_out = xid ;
* lock ) .
}
*/
if ( freeze_xmax & & ! HEAP_XMAX_IS_LOCKED_ONLY ( tuple - > t_infomask ) & &
TransactionIdDidCommit ( xid ) )
ereport ( ERROR ,
( errcode ( ERRCODE_DATA_CORRUPTED ) ,
errmsg_internal ( " cannot freeze committed xmax %u " ,
xid ) ) ) ;
}
}
else if ( ! TransactionIdIsValid ( xid ) )
else if ( ! TransactionIdIsValid ( xid ) )
{
{
@ -6678,6 +6705,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
* failed ; whereas a non - dead MOVED_IN tuple must mean the xvac
* failed ; whereas a non - dead MOVED_IN tuple must mean the xvac
* transaction succeeded .
* transaction succeeded .
*/
*/
Assert ( pagefrz - > freeze_required ) ;
if ( tuple - > t_infomask & HEAP_MOVED_OFF )
if ( tuple - > t_infomask & HEAP_MOVED_OFF )
frz - > frzflags | = XLH_INVALID_XVAC ;
frz - > frzflags | = XLH_INVALID_XVAC ;
else
else
@ -6686,8 +6714,9 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
if ( replace_xmax )
if ( replace_xmax )
{
{
Assert ( ! xmax_already_frozen & & ! freeze_xmax ) ;
Assert ( ! xmax_already_frozen & & ! freeze_xmax ) ;
Assert ( pagefrz - > freeze_required ) ;
/* Already set t_infomask/t_infomask2 flags in freeze plan */
/* Already set replace_xmax flags in freeze plan earlier */
}
}
if ( freeze_xmax )
if ( freeze_xmax )
{
{
@ -6708,13 +6737,23 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
/*
/*
* Determine if this tuple is already totally frozen , or will become
* Determine if this tuple is already totally frozen , or will become
* totally frozen
* totally frozen ( provided caller executes freeze plans for the page )
*/
*/
* totally_frozen = ( ( freeze_xmin | | xmin_already_frozen ) & &
* totally_frozen = ( ( freeze_xmin | | xmin_already_frozen ) & &
( freeze_xmax | | xmax_already_frozen ) ) ;
( freeze_xmax | | xmax_already_frozen ) ) ;
/* A "totally_frozen" tuple must not leave anything behind in xmax */
if ( ! pagefrz - > freeze_required & & ! ( xmin_already_frozen & &
Assert ( ! * totally_frozen | | ! replace_xmax ) ;
xmax_already_frozen ) )
{
/*
* So far no previous tuple from the page made freezing mandatory .
* Does this tuple force caller to freeze the entire page ?
*/
pagefrz - > freeze_required =
heap_tuple_should_freeze ( tuple , cutoffs ,
& pagefrz - > NoFreezePageRelfrozenXid ,
& pagefrz - > NoFreezePageRelminMxid ) ;
}
/* Tell caller if this tuple has a usable freeze plan set in *frz */
/* Tell caller if this tuple has a usable freeze plan set in *frz */
return freeze_xmin | | replace_xvac | | replace_xmax | | freeze_xmax ;
return freeze_xmin | | replace_xvac | | replace_xmax | | freeze_xmax ;
@ -6760,13 +6799,12 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, HeapTupleFreeze *frz)
*/
*/
void
void
heap_freeze_execute_prepared ( Relation rel , Buffer buffer ,
heap_freeze_execute_prepared ( Relation rel , Buffer buffer ,
TransactionId FreezeLimit ,
TransactionId snapshotConflictHorizon ,
HeapTupleFreeze * tuples , int ntuples )
HeapTupleFreeze * tuples , int ntuples )
{
{
Page page = BufferGetPage ( buffer ) ;
Page page = BufferGetPage ( buffer ) ;
Assert ( ntuples > 0 ) ;
Assert ( ntuples > 0 ) ;
Assert ( TransactionIdIsNormal ( FreezeLimit ) ) ;
START_CRIT_SECTION ( ) ;
START_CRIT_SECTION ( ) ;
@ -6789,19 +6827,10 @@ heap_freeze_execute_prepared(Relation rel, Buffer buffer,
int nplans ;
int nplans ;
xl_heap_freeze_page xlrec ;
xl_heap_freeze_page xlrec ;
XLogRecPtr recptr ;
XLogRecPtr recptr ;
TransactionId snapshotConflictHorizon ;
/* Prepare deduplicated representation for use in WAL record */
/* Prepare deduplicated representation for use in WAL record */
nplans = heap_xlog_freeze_plan ( tuples , ntuples , plans , offsets ) ;
nplans = heap_xlog_freeze_plan ( tuples , ntuples , plans , offsets ) ;
/*
* FreezeLimit is ( approximately ) the first XID not frozen by VACUUM .
* Back up caller ' s FreezeLimit to avoid false conflicts when
* FreezeLimit is precisely equal to VACUUM ' s OldestXmin cutoff .
*/
snapshotConflictHorizon = FreezeLimit ;
TransactionIdRetreat ( snapshotConflictHorizon ) ;
xlrec . snapshotConflictHorizon = snapshotConflictHorizon ;
xlrec . snapshotConflictHorizon = snapshotConflictHorizon ;
xlrec . nplans = nplans ;
xlrec . nplans = nplans ;
@ -6842,8 +6871,7 @@ heap_freeze_tuple(HeapTupleHeader tuple,
bool do_freeze ;
bool do_freeze ;
bool totally_frozen ;
bool totally_frozen ;
struct VacuumCutoffs cutoffs ;
struct VacuumCutoffs cutoffs ;
TransactionId NewRelfrozenXid = FreezeLimit ;
HeapPageFreeze pagefrz ;
MultiXactId NewRelminMxid = MultiXactCutoff ;
cutoffs . relfrozenxid = relfrozenxid ;
cutoffs . relfrozenxid = relfrozenxid ;
cutoffs . relminmxid = relminmxid ;
cutoffs . relminmxid = relminmxid ;
@ -6852,9 +6880,14 @@ heap_freeze_tuple(HeapTupleHeader tuple,
cutoffs . FreezeLimit = FreezeLimit ;
cutoffs . FreezeLimit = FreezeLimit ;
cutoffs . MultiXactCutoff = MultiXactCutoff ;
cutoffs . MultiXactCutoff = MultiXactCutoff ;
pagefrz . freeze_required = true ;
pagefrz . FreezePageRelfrozenXid = FreezeLimit ;
pagefrz . FreezePageRelminMxid = MultiXactCutoff ;
pagefrz . NoFreezePageRelfrozenXid = FreezeLimit ;
pagefrz . NoFreezePageRelminMxid = MultiXactCutoff ;
do_freeze = heap_prepare_freeze_tuple ( tuple , & cutoffs ,
do_freeze = heap_prepare_freeze_tuple ( tuple , & cutoffs ,
& frz , & totally_frozen ,
& pagefrz , & frz , & totally_frozen ) ;
& NewRelfrozenXid , & NewRelminMxid ) ;
/*
/*
* Note that because this is not a WAL - logged operation , we don ' t need to
* Note that because this is not a WAL - logged operation , we don ' t need to
@ -7277,22 +7310,24 @@ heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple)
}
}
/*
/*
* heap_tuple_w ould_freeze
* heap_tuple_sh ould_freeze
*
*
* Return value indicates if heap_prepare_freeze_tuple sibling function would
* Return value indicates if heap_prepare_freeze_tuple sibling function would
* freeze any of the XID / MXID fields from the tuple , given the same cutoffs .
* ( or should ) force freezing of the heap page that contains caller ' s tuple .
* We must also deal with dead tuples here , since ( xmin , xmax , xvac ) fields
* Tuple header XIDs / MXIDs < FreezeLimit / MultiXactCutoff trigger freezing .
* could be processed by pruning away the whole tuple instead of freezing .
* This includes ( xmin , xmax , xvac ) fields , as well as MultiXact member XIDs .
*
*
* The * relfrozenxid_out and * relminmxid_out input / output arguments work just
* The * NoFreezePageRelfrozenXid and * NoFreezePageRelminMxid input / output
* like the heap_prepare_freeze_tuple arguments that they ' re based on . We
* arguments help VACUUM track the oldest extant XID / MXID remaining in rel .
* never freeze here , which makes tracking the oldest extant XID / MXID simple .
* Our working assumption is that caller won ' t decide to freeze this tuple .
* It ' s up to caller to only ratchet back its own top - level trackers after the
* point that it fully commits to not freezing the tuple / page in question .
*/
*/
bool
bool
heap_tuple_w ould_freeze ( HeapTupleHeader tuple ,
heap_tuple_sh ould_freeze ( HeapTupleHeader tuple ,
const struct VacuumCutoffs * cutoffs ,
const struct VacuumCutoffs * cutoffs ,
TransactionId * relfrozenxid_out ,
TransactionId * NoFreezePageRelfrozenXid ,
MultiXactId * relminmxid_out )
MultiXactId * NoFreezePageRelminMxid )
{
{
TransactionId xid ;
TransactionId xid ;
MultiXactId multi ;
MultiXactId multi ;
@ -7303,8 +7338,8 @@ heap_tuple_would_freeze(HeapTupleHeader tuple,
if ( TransactionIdIsNormal ( xid ) )
if ( TransactionIdIsNormal ( xid ) )
{
{
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > relfrozenxid , xid ) ) ;
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > relfrozenxid , xid ) ) ;
if ( TransactionIdPrecedes ( xid , * relfrozenxid_out ) )
if ( TransactionIdPrecedes ( xid , * NoFreezePageRelfrozenXid ) )
* relfrozenxid_out = xid ;
* NoFreezePageRelfrozenXid = xid ;
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
freeze = true ;
freeze = true ;
}
}
@ -7321,8 +7356,8 @@ heap_tuple_would_freeze(HeapTupleHeader tuple,
{
{
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > relfrozenxid , xid ) ) ;
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > relfrozenxid , xid ) ) ;
/* xmax is a non-permanent XID */
/* xmax is a non-permanent XID */
if ( TransactionIdPrecedes ( xid , * relfrozenxid_out ) )
if ( TransactionIdPrecedes ( xid , * NoFreezePageRelfrozenXid ) )
* relfrozenxid_out = xid ;
* NoFreezePageRelfrozenXid = xid ;
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
freeze = true ;
freeze = true ;
}
}
@ -7333,8 +7368,8 @@ heap_tuple_would_freeze(HeapTupleHeader tuple,
else if ( HEAP_LOCKED_UPGRADED ( tuple - > t_infomask ) )
else if ( HEAP_LOCKED_UPGRADED ( tuple - > t_infomask ) )
{
{
/* xmax is a pg_upgrade'd MultiXact, which can't have updater XID */
/* xmax is a pg_upgrade'd MultiXact, which can't have updater XID */
if ( MultiXactIdPrecedes ( multi , * relminmxid_out ) )
if ( MultiXactIdPrecedes ( multi , * NoFreezePageRelminMxid ) )
* relminmxid_out = multi ;
* NoFreezePageRelminMxid = multi ;
/* heap_prepare_freeze_tuple always freezes pg_upgrade'd xmax */
/* heap_prepare_freeze_tuple always freezes pg_upgrade'd xmax */
freeze = true ;
freeze = true ;
}
}
@ -7345,8 +7380,8 @@ heap_tuple_would_freeze(HeapTupleHeader tuple,
int nmembers ;
int nmembers ;
Assert ( MultiXactIdPrecedesOrEquals ( cutoffs - > relminmxid , multi ) ) ;
Assert ( MultiXactIdPrecedesOrEquals ( cutoffs - > relminmxid , multi ) ) ;
if ( MultiXactIdPrecedes ( multi , * relminmxid_out ) )
if ( MultiXactIdPrecedes ( multi , * NoFreezePageRelminMxid ) )
* relminmxid_out = multi ;
* NoFreezePageRelminMxid = multi ;
if ( MultiXactIdPrecedes ( multi , cutoffs - > MultiXactCutoff ) )
if ( MultiXactIdPrecedes ( multi , cutoffs - > MultiXactCutoff ) )
freeze = true ;
freeze = true ;
@ -7358,8 +7393,8 @@ heap_tuple_would_freeze(HeapTupleHeader tuple,
{
{
xid = members [ i ] . xid ;
xid = members [ i ] . xid ;
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > relfrozenxid , xid ) ) ;
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > relfrozenxid , xid ) ) ;
if ( TransactionIdPrecedes ( xid , * relfrozenxid_out ) )
if ( TransactionIdPrecedes ( xid , * NoFreezePageRelfrozenXid ) )
* relfrozenxid_out = xid ;
* NoFreezePageRelfrozenXid = xid ;
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
if ( TransactionIdPrecedes ( xid , cutoffs - > FreezeLimit ) )
freeze = true ;
freeze = true ;
}
}
@ -7373,9 +7408,9 @@ heap_tuple_would_freeze(HeapTupleHeader tuple,
if ( TransactionIdIsNormal ( xid ) )
if ( TransactionIdIsNormal ( xid ) )
{
{
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > relfrozenxid , xid ) ) ;
Assert ( TransactionIdPrecedesOrEquals ( cutoffs - > relfrozenxid , xid ) ) ;
if ( TransactionIdPrecedes ( xid , * relfrozenxid_out ) )
if ( TransactionIdPrecedes ( xid , * NoFreezePageRelfrozenXid ) )
* relfrozenxid_out = xid ;
* NoFreezePageRelfrozenXid = xid ;
/* heap_prepare_freeze_tuple always freezes xvac */
/* heap_prepare_freeze_tuple forces xvac freezing */
freeze = true ;
freeze = true ;
}
}
}
}