@@ -14,9 +14,11 @@ module Database.Postgres
1414 , queryOne , queryOne_
1515 , withConnection
1616 , withClient
17+ , withTransaction
1718 ) where
1819
1920import Control.Alt
21+ import Control.Apply ((*>))
2022import Control.Monad.Eff
2123import Control.Monad.Trans
2224import Data.Either
@@ -28,7 +30,7 @@ import Data.Maybe
2830import Control.Monad.Aff
2931import Control.Monad.Eff.Class
3032import Control.Monad.Eff.Exception (Error (), error )
31- import Control.Monad.Error.Class (throwError )
33+ import Control.Monad.Error.Class (throwError , catchError )
3234import Data.Traversable (sequence )
3335
3436import Database.Postgres.SqlValue
@@ -130,6 +132,27 @@ withClient :: forall eff a
130132 -> Aff (db :: DB | eff ) a
131133withClient info p = runFn2 _withClient (mkConnectionString info) p
132134
135+ -- | Runs an asynchronous action in a database transaction. The transaction
136+ -- | will be rolled back if the computation fails and committed otherwise.
137+ -- |
138+ -- | Here the first insert will be rolled back:
139+ -- |
140+ -- | ```purescript
141+ -- | moneyTransfer :: forall e. (Client -> Aff e Unit) -> Client -> Aff e Unit
142+ -- | moneyTransfer = withTransaction $ \c -> do
143+ -- | execute_ (Query "insert into accounts ...") c
144+ -- | throwError $ error "Something went wrong"
145+ -- | execute_ (Query "insert into accounts ...") c
146+ -- | ```
147+ withTransaction :: forall eff a . (Client -> Aff (db :: DB | eff ) a ) -> Client -> Aff (db :: DB | eff ) a
148+ withTransaction act client = do
149+ execute_ (Query " BEGIN TRANSACTION" ) client
150+ res <- attempt (act client)
151+ either rollback commit res
152+ where
153+ rollback err = execute_ (Query " ROLLBACK" ) client *> throwError err
154+ commit v = execute_ (Query " COMMIT" ) client *> pure v
155+
133156liftError :: forall e a . ForeignError -> Aff e a
134157liftError err = throwError $ error (show err)
135158
0 commit comments