Skip to content

Commit 8dd1ce0

Browse files
committed
Merge pull request #9 from anttih/transactions
Transactions
2 parents 52d4185 + 044d954 commit 8dd1ce0

File tree

4 files changed

+103
-3
lines changed

4 files changed

+103
-3
lines changed

MODULE.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,47 @@ instance isSqlValueMaybe :: (IsSqlValue a) => IsSqlValue (Maybe a)
196196

197197

198198

199+
## Module Database.Postgres.Transaction
200+
201+
#### `withTransaction`
202+
203+
``` purescript
204+
withTransaction :: forall eff a. (Client -> Aff (db :: DB | eff) a) -> Client -> Aff (db :: DB | eff) a
205+
```
206+
207+
Runs an asynchronous action in a database transaction. The transaction
208+
will be rolled back if the computation fails and committed otherwise.
209+
210+
Here the first insert will be rolled back:
211+
212+
```purescript
213+
moneyTransfer :: forall e. (Client -> Aff e Unit) -> Client -> Aff e Unit
214+
moneyTransfer = withTransaction $ \c -> do
215+
execute_ (Query "insert into accounts ...") c
216+
throwError $ error "Something went wrong"
217+
execute_ (Query "insert into accounts ...") c
218+
```
219+
220+
#### `begin`
221+
222+
``` purescript
223+
begin :: forall eff. Client -> Aff (db :: DB | eff) Unit
224+
```
225+
226+
227+
#### `commit`
228+
229+
``` purescript
230+
commit :: forall eff. Client -> Aff (db :: DB | eff) Unit
231+
```
232+
233+
234+
#### `rollback`
235+
236+
``` purescript
237+
rollback :: forall eff. Client -> Aff (db :: DB | eff) Unit
238+
```
239+
240+
241+
199242

src/Database/Postgres.purs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module Database.Postgres
1717
) where
1818

1919
import Control.Alt
20+
import Control.Apply ((*>))
2021
import Control.Monad.Eff
2122
import Control.Monad.Trans
2223
import Data.Either
@@ -28,7 +29,7 @@ import Data.Maybe
2829
import Control.Monad.Aff
2930
import Control.Monad.Eff.Class
3031
import Control.Monad.Eff.Exception(Error(), error)
31-
import Control.Monad.Error.Class (throwError)
32+
import Control.Monad.Error.Class (throwError, catchError)
3233
import Data.Traversable (sequence)
3334

3435
import Database.Postgres.SqlValue
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
module Database.Postgres.Transaction where
2+
3+
import Control.Apply ((*>))
4+
import Control.Monad.Aff
5+
import Control.Monad.Error.Class (throwError)
6+
import Data.Either
7+
8+
import Database.Postgres
9+
import Database.Postgres.SqlValue
10+
11+
-- | Runs an asynchronous action in a database transaction. The transaction
12+
-- | will be rolled back if the computation fails and committed otherwise.
13+
-- |
14+
-- | Here the first insert will be rolled back:
15+
-- |
16+
-- | ```purescript
17+
-- | moneyTransfer :: forall e. (Client -> Aff e Unit) -> Client -> Aff e Unit
18+
-- | moneyTransfer = withTransaction $ \c -> do
19+
-- | execute_ (Query "insert into accounts ...") c
20+
-- | throwError $ error "Something went wrong"
21+
-- | execute_ (Query "insert into accounts ...") c
22+
-- | ```
23+
withTransaction :: forall eff a. (Client -> Aff (db :: DB | eff) a) -> Client -> Aff (db :: DB | eff) a
24+
withTransaction act client = do
25+
begin client
26+
res <- attempt (act client)
27+
either rollback_ commit_ res
28+
where
29+
rollback_ err = rollback client *> throwError err
30+
commit_ v = commit client *> pure v
31+
32+
begin :: forall eff. Client -> Aff (db :: DB | eff) Unit
33+
begin = execute_ (Query "BEGIN TRANSACTION")
34+
35+
commit :: forall eff. Client -> Aff (db :: DB | eff) Unit
36+
commit = execute_ (Query "COMMIT")
37+
38+
rollback :: forall eff. Client -> Aff (db :: DB | eff) Unit
39+
rollback = execute_ (Query "ROLLBACK")

test/Main.purs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
module Test.Main where
22

3-
import Database.Postgres
4-
import Database.Postgres.SqlValue
53
import Debug.Trace
64
import Control.Monad.Eff
75
import Control.Monad.Eff.Class
86
import Control.Monad.Cont.Trans
97
import Control.Monad.Trans
8+
import Control.Monad.Error.Class (throwError)
9+
import Control.Monad.Eff.Exception (error)
1010
import Data.Array
1111
import Data.Foldable
1212
import Data.Either
@@ -16,6 +16,10 @@ import Data.Foreign.Class
1616
import Data.Foreign.Index
1717
import Control.Monad.Aff
1818

19+
import Database.Postgres
20+
import Database.Postgres.SqlValue
21+
import Database.Postgres.Transaction
22+
1923
main = runAff (trace <<< show) (const $ trace "All ok") $ do
2024
liftEff <<< trace $ "connecting to " <> mkConnectionString connectionInfo <> "..."
2125
exampleUsingWithConnection
@@ -26,6 +30,8 @@ main = runAff (trace <<< show) (const $ trace "All ok") $ do
2630

2731
exampleQueries
2832

33+
exampleTransaction
34+
2935
liftEff $ disconnect
3036

3137
data Artist = Artist
@@ -74,6 +80,17 @@ exampleQueries = withClient connectionInfo $ \c -> do
7480
artists <- query (Query "select * from artist where name = $1" :: Query Artist) [toSql "Toto"] c
7581
liftEff $ printRows artists
7682

83+
exampleTransaction :: forall eff. Aff (db :: DB | eff) Unit
84+
exampleTransaction = withConnection connectionInfo $ \c -> do
85+
execute_ (Query "delete from artist") c
86+
apathize $ tryInsert c
87+
one <- queryOne_ (Query "select * from artist" :: Query Artist) c
88+
liftEff $ trace $ show one
89+
where
90+
tryInsert = withTransaction $ \c -> do
91+
execute_ (Query "insert into artist values ('Not there', 1999)") c
92+
throwError $ error "fail"
93+
7794
printRows :: forall a eff. (Show a) => [a] -> Eff (trace :: Trace | eff) Unit
7895
printRows rows = trace $ "result:\n" <> foldMap stringify rows
7996
where stringify = show >>> flip (<>) "\n"

0 commit comments

Comments
 (0)