hasql-transaction-0.4.2: library/Hasql/Transaction.hs
module Hasql.Transaction
(
-- * Transaction settings
Mode(..),
IsolationLevel(..),
-- * Transaction monad
Transaction,
run,
sql,
query,
)
where
import Hasql.Transaction.Prelude
import qualified Hasql.Query as Query
import qualified Hasql.Session as Session
import qualified Hasql.Transaction.Queries as Queries
import qualified PostgreSQL.ErrorCodes as ErrorCodes
-- |
-- A composable abstraction over the retryable transactions.
--
-- Executes multiple queries under the specified mode and isolation level,
-- while automatically retrying the transaction in case of conflicts.
-- Thus this abstraction closely reproduces the behaviour of 'STM'.
newtype Transaction a =
Transaction (Session.Session a)
deriving (Functor, Applicative, Monad)
-- |
--
data Mode =
-- |
-- Read-only. No writes possible.
Read |
-- |
-- Write and commit.
Write |
-- |
-- Write without committing.
-- Useful for testing,
-- allowing you to modify your database,
-- producing some result based on your changes,
-- and letting Hasql roll all the changes back on the exit from the transaction.
WriteWithoutCommitting
deriving (Show, Eq, Ord, Enum, Bounded)
-- |
-- For reference see
-- <http://www.postgresql.org/docs/current/static/transaction-iso.html the Postgres' documentation>.
--
data IsolationLevel =
ReadCommitted |
RepeatableRead |
Serializable
deriving (Show, Eq, Ord, Enum, Bounded)
-- |
-- Execute the transaction using the provided isolation level, mode and database connection.
{-# INLINABLE run #-}
run :: Transaction a -> IsolationLevel -> Mode -> Session.Session a
run (Transaction session) isolation mode =
do
Session.query () (Queries.beginTransaction mode')
resultEither <- tryError session
case resultEither of
Left error ->
case error of
Session.ResultError (Session.ServerError code _ _ _) | code == ErrorCodes.serialization_failure ->
run (Transaction session) isolation mode
_ ->
throwError error
Right result -> do
Session.query () $ bool Queries.abortTransaction Queries.commitTransaction commit
pure result
where
mode' =
(unsafeCoerce isolation, write)
(write, commit) =
case mode of
Read -> (False, True)
Write -> (True, True)
WriteWithoutCommitting -> (True, False)
-- |
-- Possibly a multi-statement query,
-- which however cannot be parameterized or prepared,
-- nor can any results of it be collected.
{-# INLINE sql #-}
sql :: ByteString -> Transaction ()
sql sql =
Transaction $ Session.sql sql
-- |
-- Parameters and a specification of the parametric query to apply them to.
{-# INLINE query #-}
query :: a -> Query.Query a b -> Transaction b
query params query =
Transaction $ Session.query params query