Fix TwoPhaseGetDummyBackendId().

This was broken in commit ed0b409d22346b1b027a4c2099ca66984d94b6dd,
which revised the GlobalTransactionData struct to not include the
associated PGPROC as its first member, but overlooked one place where
a cast was used in reliance on that equivalence.

The most effective way of fixing this seems to be to create a new function
that looks up the GlobalTransactionData struct given the XID, and make
both TwoPhaseGetDummyBackendId and TwoPhaseGetDummyProc rely on that.

Per report from Robert Ross.
This commit is contained in:
Tom Lane 2012-08-08 11:52:06 -04:00
parent 7d947ec82a
commit 5cf2307c98

View File

@ -107,8 +107,8 @@ int max_prepared_xacts = 0;
typedef struct GlobalTransactionData typedef struct GlobalTransactionData
{ {
GlobalTransaction next; GlobalTransaction next; /* list link for free list */
int pgprocno; /* dummy proc */ int pgprocno; /* ID of associated dummy PGPROC */
BackendId dummyBackendId; /* similar to backend id for backends */ BackendId dummyBackendId; /* similar to backend id for backends */
TimestampTz prepared_at; /* time of preparation */ TimestampTz prepared_at; /* time of preparation */
XLogRecPtr prepare_lsn; /* XLOG offset of prepare record */ XLogRecPtr prepare_lsn; /* XLOG offset of prepare record */
@ -202,10 +202,13 @@ TwoPhaseShmemInit(void)
sizeof(GlobalTransaction) * max_prepared_xacts)); sizeof(GlobalTransaction) * max_prepared_xacts));
for (i = 0; i < max_prepared_xacts; i++) for (i = 0; i < max_prepared_xacts; i++)
{ {
gxacts[i].pgprocno = PreparedXactProcs[i].pgprocno; /* insert into linked list */
gxacts[i].next = TwoPhaseState->freeGXacts; gxacts[i].next = TwoPhaseState->freeGXacts;
TwoPhaseState->freeGXacts = &gxacts[i]; TwoPhaseState->freeGXacts = &gxacts[i];
/* associate it with a PGPROC assigned by InitProcGlobal */
gxacts[i].pgprocno = PreparedXactProcs[i].pgprocno;
/* /*
* Assign a unique ID for each dummy proc, so that the range of * Assign a unique ID for each dummy proc, so that the range of
* dummy backend IDs immediately follows the range of normal * dummy backend IDs immediately follows the range of normal
@ -300,7 +303,7 @@ MarkAsPreparing(TransactionId xid, const char *gid,
errhint("Increase max_prepared_transactions (currently %d).", errhint("Increase max_prepared_transactions (currently %d).",
max_prepared_xacts))); max_prepared_xacts)));
gxact = TwoPhaseState->freeGXacts; gxact = TwoPhaseState->freeGXacts;
TwoPhaseState->freeGXacts = (GlobalTransaction) gxact->next; TwoPhaseState->freeGXacts = gxact->next;
proc = &ProcGlobal->allProcs[gxact->pgprocno]; proc = &ProcGlobal->allProcs[gxact->pgprocno];
pgxact = &ProcGlobal->allPgXact[gxact->pgprocno]; pgxact = &ProcGlobal->allPgXact[gxact->pgprocno];
@ -680,40 +683,25 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
} }
/* /*
* TwoPhaseGetDummyProc * TwoPhaseGetGXact
* Get the dummy backend ID for prepared transaction specified by XID * Get the GlobalTransaction struct for a prepared transaction
* * specified by XID
* Dummy backend IDs are similar to real backend IDs of real backends.
* They start at MaxBackends + 1, and are unique across all currently active
* real backends and prepared transactions.
*/ */
BackendId static GlobalTransaction
TwoPhaseGetDummyBackendId(TransactionId xid) TwoPhaseGetGXact(TransactionId xid)
{ {
PGPROC *proc = TwoPhaseGetDummyProc(xid); GlobalTransaction result = NULL;
return ((GlobalTransaction) proc)->dummyBackendId;
}
/*
* TwoPhaseGetDummyProc
* Get the PGPROC that represents a prepared transaction specified by XID
*/
PGPROC *
TwoPhaseGetDummyProc(TransactionId xid)
{
PGPROC *result = NULL;
int i; int i;
static TransactionId cached_xid = InvalidTransactionId; static TransactionId cached_xid = InvalidTransactionId;
static PGPROC *cached_proc = NULL; static GlobalTransaction cached_gxact = NULL;
/* /*
* During a recovery, COMMIT PREPARED, or ABORT PREPARED, we'll be called * During a recovery, COMMIT PREPARED, or ABORT PREPARED, we'll be called
* repeatedly for the same XID. We can save work with a simple cache. * repeatedly for the same XID. We can save work with a simple cache.
*/ */
if (xid == cached_xid) if (xid == cached_xid)
return cached_proc; return cached_gxact;
LWLockAcquire(TwoPhaseStateLock, LW_SHARED); LWLockAcquire(TwoPhaseStateLock, LW_SHARED);
@ -724,7 +712,7 @@ TwoPhaseGetDummyProc(TransactionId xid)
if (pgxact->xid == xid) if (pgxact->xid == xid)
{ {
result = &ProcGlobal->allProcs[gxact->pgprocno]; result = gxact;
break; break;
} }
} }
@ -732,14 +720,42 @@ TwoPhaseGetDummyProc(TransactionId xid)
LWLockRelease(TwoPhaseStateLock); LWLockRelease(TwoPhaseStateLock);
if (result == NULL) /* should not happen */ if (result == NULL) /* should not happen */
elog(ERROR, "failed to find dummy PGPROC for xid %u", xid); elog(ERROR, "failed to find GlobalTransaction for xid %u", xid);
cached_xid = xid; cached_xid = xid;
cached_proc = result; cached_gxact = result;
return result; return result;
} }
/*
* TwoPhaseGetDummyProc
* Get the dummy backend ID for prepared transaction specified by XID
*
* Dummy backend IDs are similar to real backend IDs of real backends.
* They start at MaxBackends + 1, and are unique across all currently active
* real backends and prepared transactions.
*/
BackendId
TwoPhaseGetDummyBackendId(TransactionId xid)
{
GlobalTransaction gxact = TwoPhaseGetGXact(xid);
return gxact->dummyBackendId;
}
/*
* TwoPhaseGetDummyProc
* Get the PGPROC that represents a prepared transaction specified by XID
*/
PGPROC *
TwoPhaseGetDummyProc(TransactionId xid)
{
GlobalTransaction gxact = TwoPhaseGetGXact(xid);
return &ProcGlobal->allProcs[gxact->pgprocno];
}
/************************************************************************/ /************************************************************************/
/* State file support */ /* State file support */
/************************************************************************/ /************************************************************************/