*** a/src/backend/storage/lmgr/predicate.c --- b/src/backend/storage/lmgr/predicate.c *************** *** 244,249 **** --- 244,257 ---- #define SxactIsOnFinishedList(sxact) (!SHMQueueIsDetached(&((sxact)->finishedLink))) + /* + * Note that a sxact is marked "prepared" once it has passed + * PreCommit_CheckForSerializationFailure, even if it isn't using + * 2PC. This is the point at which it can no longer be aborted. + * + * The PREPARED flag remains set after commit, so SxactIsCommitted + * implies SxactIsPrepared. + */ #define SxactIsCommitted(sxact) (((sxact)->flags & SXACT_FLAG_COMMITTED) != 0) #define SxactIsPrepared(sxact) (((sxact)->flags & SXACT_FLAG_PREPARED) != 0) #define SxactIsRolledBack(sxact) (((sxact)->flags & SXACT_FLAG_ROLLED_BACK) != 0) *************** *** 3165,3170 **** ReleasePredicateLocks(bool isCommit) --- 3173,3185 ---- */ MySerializableXact->flags |= SXACT_FLAG_DOOMED; MySerializableXact->flags |= SXACT_FLAG_ROLLED_BACK; + /* + * If the transaction was previously prepared, but is now failing due + * to a ROLLBACK PREPARED or (hopefully very rare) error after the + * prepare, clear the prepared flag. This simplifies conflict + * checking, + */ + MySerializableXact->flags &= ~SXACT_FLAG_PREPARED; } if (!topLevelIsDeclaredReadOnly) *************** *** 4381,4387 **** OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader, { SERIALIZABLEXACT *t2 = conflict->sxactIn; ! if (SxactIsCommitted(t2) && (!SxactIsCommitted(reader) || t2->commitSeqNo <= reader->commitSeqNo) && (!SxactIsCommitted(writer) --- 4396,4402 ---- { SERIALIZABLEXACT *t2 = conflict->sxactIn; ! if (SxactIsPrepared(t2) && (!SxactIsCommitted(reader) || t2->commitSeqNo <= reader->commitSeqNo) && (!SxactIsCommitted(writer) *************** *** 4400,4406 **** OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader, } /*------------------------------------------------------------------------ ! * Check whether the reader has become a pivot with a committed writer: * * T0 ------> R ------> W * rw rw --- 4415,4422 ---- } /*------------------------------------------------------------------------ ! * Check whether the reader has become a pivot with a writer ! * that's committed or prepared: * * T0 ------> R ------> W * rw rw *************** *** 4411,4417 **** OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader, * - T0 is READ ONLY, and overlaps the writer *------------------------------------------------------------------------ */ ! if (!failure && SxactIsCommitted(writer) && !SxactIsReadOnly(reader)) { if (SxactHasSummaryConflictIn(reader)) { --- 4427,4433 ---- * - T0 is READ ONLY, and overlaps the writer *------------------------------------------------------------------------ */ ! if (!failure && SxactIsPrepared(writer) && !SxactIsReadOnly(reader)) { if (SxactHasSummaryConflictIn(reader)) { *** a/src/test/regress/expected/prepared_xacts.out --- b/src/test/regress/expected/prepared_xacts.out *************** *** 131,142 **** SELECT * FROM pxtest1; ddd (2 rows) - INSERT INTO pxtest1 VALUES ('fff'); -- This should fail, because the two transactions have a write-skew anomaly ! PREPARE TRANSACTION 'foo5'; ERROR: could not serialize access due to read/write dependencies among transactions ! DETAIL: Canceled on commit attempt with conflict in from prepared pivot. HINT: The transaction might succeed if retried. SELECT gid FROM pg_prepared_xacts; gid ------ --- 131,142 ---- ddd (2 rows) -- This should fail, because the two transactions have a write-skew anomaly ! INSERT INTO pxtest1 VALUES ('fff'); ERROR: could not serialize access due to read/write dependencies among transactions ! DETAIL: Cancelled on identification as a pivot, during write. HINT: The transaction might succeed if retried. + PREPARE TRANSACTION 'foo5'; SELECT gid FROM pg_prepared_xacts; gid ------ *** a/src/test/regress/expected/prepared_xacts_1.out --- b/src/test/regress/expected/prepared_xacts_1.out *************** *** 134,141 **** SELECT * FROM pxtest1; aaa (1 row) - INSERT INTO pxtest1 VALUES ('fff'); -- This should fail, because the two transactions have a write-skew anomaly PREPARE TRANSACTION 'foo5'; ERROR: prepared transactions are disabled HINT: Set max_prepared_transactions to a nonzero value. --- 134,141 ---- aaa (1 row) -- This should fail, because the two transactions have a write-skew anomaly + INSERT INTO pxtest1 VALUES ('fff'); PREPARE TRANSACTION 'foo5'; ERROR: prepared transactions are disabled HINT: Set max_prepared_transactions to a nonzero value. *** a/src/test/regress/sql/prepared_xacts.sql --- b/src/test/regress/sql/prepared_xacts.sql *************** *** 74,82 **** SELECT gid FROM pg_prepared_xacts; BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; SELECT * FROM pxtest1; - INSERT INTO pxtest1 VALUES ('fff'); -- This should fail, because the two transactions have a write-skew anomaly PREPARE TRANSACTION 'foo5'; SELECT gid FROM pg_prepared_xacts; --- 74,82 ---- BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; SELECT * FROM pxtest1; -- This should fail, because the two transactions have a write-skew anomaly + INSERT INTO pxtest1 VALUES ('fff'); PREPARE TRANSACTION 'foo5'; SELECT gid FROM pg_prepared_xacts;