@@ -663,6 +663,15 @@ withTransactionSerializable =
663663 { isolationLevel = Serializable
664664 , readWriteMode = ReadWrite
665665 }
666+ retryOnNotSerializable
667+ where
668+ retryOnNotSerializable exception =
669+ case exception of
670+ SqlError{..} | sqlState == serialization_failure
671+ -> True
672+ _ -> False
673+ -- http://www.postgresql.org/docs/current/static/errcodes-appendix.html
674+ serialization_failure = "40001"
666675
667676-- | Execute an action inside a SQL transaction with a given isolation level.
668677withTransactionLevel :: IsolationLevel -> Connection -> IO a -> IO a
@@ -678,14 +687,15 @@ withTransactionMode mode conn act =
678687 commit conn
679688 return r
680689
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.
690+-- | Like 'withTransactionMode', but also takes a custom callback to
691+-- determine if a transaction should be retried if an 'SqlError' occurs.
692+-- If the callback returns True, then the transaction will be retried.
693+-- If the callback returns False, or an exception other than an 'SqlError'
694+-- occurs then the transaction will be rolled back and the exception rethrown.
685695--
686696-- This is used to implement 'withTransactionSerializable'.
687−withTransactionModeRetry :: TransactionMode -> Connection -> IO a -> IO a
688−withTransactionModeRetry mode conn act =
697+withTransactionModeRetry :: TransactionMode -> (SqlError -> Bool) -> Connection -> IO a -> IO a
698+withTransactionModeRetry mode shouldRetry conn act =
689699 mask $ \restore ->
690700 retryLoop $ try $ do
691701 a <- restore act
@@ -699,16 +709,12 @@ withTransactionModeRetry mode conn act =
699709 case r of
700710 Left e -> do
701711 rollback conn
702− case fromException e of
703− Just SqlError{..} | sqlState == serialization_failure
704− -> retryLoop act'
705− _ -> throwIO e
712+ case fmap shouldRetry (fromException e) of
713+ Just True -> retryLoop act'
714+ _ -> throwIO e
706715 Right a ->
707716 return a
708717
709− -- http://www.postgresql.org/docs/current/static/errcodes-appendix.html
710− serialization_failure = "40001"
711−
712718-- | Rollback a transaction.
713719rollback :: Connection -> IO ()
714720rollback conn = execute_ conn "ABORT" >> return ()