From db108349bf7fe7fe82e2ff32e42436cfbc4f37dc Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Wed, 8 Aug 2012 11:52:02 -0400 Subject: [PATCH] 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. --- src/backend/access/transam/twophase.c | 76 ++++++++++++++++----------- 1 file changed, 46 insertions(+), 30 deletions(-) diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c index 7f198c2e3e..2a60c62dd4 100644 --- a/src/backend/access/transam/twophase.c +++ b/src/backend/access/transam/twophase.c @@ -108,8 +108,8 @@ int max_prepared_xacts = 0; typedef struct GlobalTransactionData { - GlobalTransaction next; - int pgprocno; /* dummy proc */ + GlobalTransaction next; /* list link for free list */ + int pgprocno; /* ID of associated dummy PGPROC */ BackendId dummyBackendId; /* similar to backend id for backends */ TimestampTz prepared_at; /* time of preparation */ XLogRecPtr prepare_lsn; /* XLOG offset of prepare record */ @@ -203,10 +203,13 @@ TwoPhaseShmemInit(void) sizeof(GlobalTransaction) * max_prepared_xacts)); for (i = 0; i < max_prepared_xacts; i++) { - gxacts[i].pgprocno = PreparedXactProcs[i].pgprocno; + /* insert into linked list */ gxacts[i].next = TwoPhaseState->freeGXacts; 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 * dummy backend IDs immediately follows the range of normal @@ -301,7 +304,7 @@ MarkAsPreparing(TransactionId xid, const char *gid, errhint("Increase max_prepared_transactions (currently %d).", max_prepared_xacts))); gxact = TwoPhaseState->freeGXacts; - TwoPhaseState->freeGXacts = (GlobalTransaction) gxact->next; + TwoPhaseState->freeGXacts = gxact->next; proc = &ProcGlobal->allProcs[gxact->pgprocno]; pgxact = &ProcGlobal->allPgXact[gxact->pgprocno]; @@ -680,40 +683,25 @@ pg_prepared_xact(PG_FUNCTION_ARGS) } /* - * 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. + * TwoPhaseGetGXact + * Get the GlobalTransaction struct for a prepared transaction + * specified by XID */ -BackendId -TwoPhaseGetDummyBackendId(TransactionId xid) +static GlobalTransaction +TwoPhaseGetGXact(TransactionId xid) { - PGPROC *proc = TwoPhaseGetDummyProc(xid); - - return ((GlobalTransaction) proc)->dummyBackendId; -} - -/* - * TwoPhaseGetDummyProc - * Get the PGPROC that represents a prepared transaction specified by XID - */ -PGPROC * -TwoPhaseGetDummyProc(TransactionId xid) -{ - PGPROC *result = NULL; + GlobalTransaction result = NULL; int i; 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 * repeatedly for the same XID. We can save work with a simple cache. */ if (xid == cached_xid) - return cached_proc; + return cached_gxact; LWLockAcquire(TwoPhaseStateLock, LW_SHARED); @@ -724,7 +712,7 @@ TwoPhaseGetDummyProc(TransactionId xid) if (pgxact->xid == xid) { - result = &ProcGlobal->allProcs[gxact->pgprocno]; + result = gxact; break; } } @@ -732,14 +720,42 @@ TwoPhaseGetDummyProc(TransactionId xid) LWLockRelease(TwoPhaseStateLock); 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_proc = result; + cached_gxact = 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 */ /************************************************************************/