module Hasql.Transaction
(
Mode(..),
IsolationLevel(..),
Transaction,
run,
query,
)
where
import Hasql.Transaction.Prelude
import qualified Hasql.Connection as Connection
import qualified Hasql.Query as Query
import qualified Hasql.Transaction.Queries as Queries
import qualified PostgreSQL.ErrorCodes as ErrorCodes
newtype Transaction a =
Transaction (ReaderT (Connection.Connection, IORef Int) (EitherT Query.ResultsError IO) a)
deriving (Functor, Applicative, Monad)
data Mode =
Read |
Write |
WriteWithoutCommitting
deriving (Show, Eq, Ord, Enum, Bounded)
data IsolationLevel =
ReadCommitted |
RepeatableRead |
Serializable
deriving (Show, Eq, Ord, Enum, Bounded)
run :: Transaction a -> IsolationLevel -> Mode -> Connection.Connection -> IO (Either Query.ResultsError a)
run (Transaction tx) isolation mode connection =
runEitherT $ do
EitherT $ Query.run (Queries.beginTransaction mode') () connection
counterRef <- lift $ newIORef 0
resultEither <- lift $ runEitherT $ runReaderT tx (connection, counterRef)
case resultEither of
Left (Query.ResultError (Query.ServerError code _ _ _))
| code == ErrorCodes.serialization_failure ->
EitherT $ run (Transaction tx) isolation mode connection
_ -> do
result <- EitherT $ pure resultEither
let
query =
if commit
then Queries.commitTransaction
else Queries.abortTransaction
in
EitherT $ Query.run query () connection
pure result
where
mode' =
(unsafeCoerce isolation, write)
(write, commit) =
case mode of
Read -> (False, True)
Write -> (True, True)
WriteWithoutCommitting -> (True, False)
query :: a -> Query.Query a b -> Transaction b
query params query =
Transaction $ ReaderT $ \(connection, _) -> EitherT $
Query.run query params connection