@@ -106,6 +106,7 @@ module Database.PostgreSQL.Simple
106106 , defaultReadWriteMode
107107 , withTransactionLevel
108108 , withTransactionMode
109+ , withTransactionModeRetry
109110-- , Base.autocommit
110111 , begin
111112 , beginLevel
@@ -649,19 +650,42 @@ withTransaction = withTransactionMode defaultTransactionMode
649650-- serialization failure occurs, roll back the transaction and try again.
650651-- Be warned that this may execute the IO action multiple times.
651652--
652−-- More precisely, if a 'SqlError' arises whose 'sqlState' is @\"40001\"@
653−-- (@serialization_failure@), this will issue a @ROLLBACK@, then try the action
654−-- again. If any other exception arises, this will issue a @ROLLBACK@, but
655−-- will propagate the exception instead of retrying.
656−--
657653-- A 'Serializable' transaction creates the illusion that your program has
658654-- exclusive access to the database. This means that, even in a concurrent
659655-- setting, you can perform queries in sequence without having to worry about
660656-- what might happen between one statement and the next.
661657--
662658-- Think of it as STM, but without @retry@.
663−withTransactionSerializable :: ReadWriteMode -> Connection -> IO a -> IO a
664−withTransactionSerializable readWriteMode conn act =
659+withTransactionSerializable :: Connection -> IO a -> IO a
660+withTransactionSerializable =
661+ withTransactionModeRetry
662+ TransactionMode
663+ { isolationLevel = Serializable
664+ , readWriteMode = ReadWrite
665+ }
666+
667+-- | Execute an action inside a SQL transaction with a given isolation level.
668+withTransactionLevel :: IsolationLevel -> Connection -> IO a -> IO a
669+withTransactionLevel lvl
670+ = withTransactionMode defaultTransactionMode { isolationLevel = lvl }
671+
672+-- | Execute an action inside a SQL transaction with a given transaction mode.
673+withTransactionMode :: TransactionMode -> Connection -> IO a -> IO a
674+withTransactionMode mode conn act =
675+ mask $ \restore -> do
676+ beginMode mode conn
677+ r <- restore act `onException` rollback conn
678+ commit conn
679+ return r
680+
681+-- | Like 'withTransactionMode', but if a 'SqlError' arises whose 'sqlState' is
682+-- @\"40001\"@ (@serialization_failure@), this will issue a @ROLLBACK@, then
683+-- try the action again. If any other exception arises, this will issue a
684+-- @ROLLBACK@, but will propagate the exception instead of retrying.
685+--
686+-- This is used to implement 'withTransactionSerializable'.
687+withTransactionModeRetry :: TransactionMode -> Connection -> IO a -> IO a
688+withTransactionModeRetry mode conn act =
665689 mask $ \restore ->
666690 retryLoop $ try $ do
667691 a <- restore act
@@ -682,28 +706,9 @@ withTransactionSerializable readWriteMode conn act =
682706 Right a ->
683707 return a
684708
685− mode = TransactionMode
686− { isolationLevel = Serializable
687− , readWriteMode
688− }
689−
690709 -- http://www.postgresql.org/docs/current/static/errcodes-appendix.html
691710 serialization_failure = "40001"
692711
693−-- | Execute an action inside a SQL transaction with a given isolation level.
694−withTransactionLevel :: IsolationLevel -> Connection -> IO a -> IO a
695−withTransactionLevel lvl
696− = withTransactionMode defaultTransactionMode { isolationLevel = lvl }
697−
698−-- | Execute an action inside a SQL transaction with a given transaction mode.
699−withTransactionMode :: TransactionMode -> Connection -> IO a -> IO a
700−withTransactionMode mode conn act =
701− mask $ \restore -> do
702− beginMode mode conn
703− r <- restore act `onException` rollback conn
704− commit conn
705− return r
706−
707712-- | Rollback a transaction.
708713rollback :: Connection -> IO ()
709714rollback conn = execute_ conn "ABORT" >> return ()