@@ -1,13 +1,19 @@
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2module ProjectM36.Client.Simple (
2module ProjectM36.Client.Simple (
3 simpleConnectProjectM36,
3 simpleConnectProjectM36,
4+ simpleConnectProjectM36At,
4 withTransaction,
5 withTransaction,
10 AtomType(..),
14 AtomType(..),
11 DbError(..),
17 DbError(..),
12 Attribute(..),
18 Attribute(..),
13 C.ConnectionInfo(..),
19 C.ConnectionInfo(..),
@@ -15,62 +21,67 @@ module ProjectM36.Client.Simple (
15 C.NotificationCallback,
21 C.NotificationCallback,
16 C.emptyNotificationCallback,
22 C.emptyNotificationCallback,
17 C.DatabaseContextExpr(..),
23 C.DatabaseContextExpr(..),
18− C.RelationalExprBase(..)
24+ C.RelationalExprBase(..)
20-- | A simplified client interface for Project:M36 database access.
26-- | A simplified client interface for Project:M36 database access.
21−import ProjectM36.Error
27+import Control.Exception.Base
28+import Control.Monad.Reader
22import ProjectM36.Base
29import ProjectM36.Base
23import qualified ProjectM36.Client as C
30import qualified ProjectM36.Client as C
24−import Control.Monad.Reader
31+import ProjectM36.Error
25−import Control.Exception.Base
27type DbConn = (C.SessionId, C.Connection)
33type DbConn = (C.SessionId, C.Connection)
29−newtype Db a = Db {runDB :: ReaderT DbConn IO a}
35+newtype Db a = Db {runDb :: ReaderT DbConn IO a}
30 deriving (Functor, Applicative, Monad, MonadIO)
36 deriving (Functor, Applicative, Monad, MonadIO)
32newtype TransactionCancelled = TransactionCancelled DbError deriving Show
38newtype TransactionCancelled = TransactionCancelled DbError deriving Show
33instance Exception TransactionCancelled
39instance Exception TransactionCancelled
35−-- | A simple alternative to 'connectProjectM36' which includes simple session management
41+-- | A simple alternative to 'connectProjectM36' which includes simple session management.
36−simpleConnectProjectM36 :: C.ConnectionInfo -> IO (Either DbError DbConn)
42+simpleConnectProjectM36At :: HeadName -> C.ConnectionInfo -> IO (Either DbError DbConn)
37−simpleConnectProjectM36 connInfo = do
43+simpleConnectProjectM36At headName connInfo = do
38 eConn <- C.connectProjectM36 connInfo
44 eConn <- C.connectProjectM36 connInfo
39 case eConn of
45 case eConn of
40 Left err -> pure (Left (ConnError err))
46 Left err -> pure (Left (ConnError err))
41 Right conn -> do
47 Right conn -> do
42− eSess <- C.createSessionAtHead conn "master"
48+ eSess <- C.createSessionAtHead conn headName
43 case eSess of
49 case eSess of
44 Left err -> do
50 Left err -> do
45 C.close conn
51 C.close conn
46 pure (Left (RelError err))
52 pure (Left (RelError err))
47 Right sess -> pure (Right (sess, conn))
53 Right sess -> pure (Right (sess, conn))
55+-- | Same as 'simpleConnectProjectM36At' but always connects to the @master@ branch.
56+simpleConnectProjectM36 :: C.ConnectionInfo -> IO (Either DbError DbConn)
57+simpleConnectProjectM36 = simpleConnectProjectM36At "master"
59+-- | Closes the database connection.
49−close :: DbConn -> IO ()
60+close :: DbConn -> IO ()
50close (_ , conn) = C.close conn
61close (_ , conn) = C.close conn
52-- | Runs a Db monad which may include some database updates. If an exception or error occurs, the transaction is rolled back. Otherwise, the transaction is committed to the head of the current branch.
63-- | Runs a Db monad which may include some database updates. If an exception or error occurs, the transaction is rolled back. Otherwise, the transaction is committed to the head of the current branch.
53withTransaction :: DbConn -> Db a -> IO (Either DbError a)
64withTransaction :: DbConn -> Db a -> IO (Either DbError a)
54−withTransaction sessconn = withTransactionUsing sessconn UnionMergeStrategy
65+withTransaction sessconn = withTransactionUsing sessconn UnionMergeStrategy
56-- | Same a 'withTransaction' except that the merge strategy can be specified.
67-- | Same a 'withTransaction' except that the merge strategy can be specified.
57withTransactionUsing :: DbConn -> MergeStrategy -> Db a -> IO (Either DbError a)
68withTransactionUsing :: DbConn -> MergeStrategy -> Db a -> IO (Either DbError a)
58withTransactionUsing (sess, conn) strat dbm = do
69withTransactionUsing (sess, conn) strat dbm = do
59 eHeadName <- C.headName sess conn
70 eHeadName <- C.headName sess conn
60− case eHeadName of
71+ case eHeadName of
61 Left err -> pure (Left (RelError err))
72 Left err -> pure (Left (RelError err))
62 Right headName -> do
73 Right headName -> do
63 let successFunc = C.autoMergeToHead sess conn strat headName
74 let successFunc = C.autoMergeToHead sess conn strat headName
64− block = runReaderT (runDB dbm) (sess, conn)
75+ block = runReaderT (runDb dbm) (sess, conn)
65 handler :: TransactionCancelled -> IO (Either DbError a)
76 handler :: TransactionCancelled -> IO (Either DbError a)
66 handler (TransactionCancelled err) = pure (Left err)
77 handler (TransactionCancelled err) = pure (Left err)
67 handle handler $ do
78 handle handler $ do
68 ret <- C.withTransaction sess conn (block >>= pure . Right) successFunc
79 ret <- C.withTransaction sess conn (block >>= pure . Right) successFunc
69− case ret of
80+ case ret of
70− Left err -> pure (Left (RelError err))
81+ Left err -> pure (Left (RelError err))
71 Right val -> pure (Right val)
82 Right val -> pure (Right val)
73−-- | A union of connection and other errors that can be returned from 'withDBConnection'.
84+-- | A union of connection and other errors that can be returned from 'withTransaction'.
74data DbError = ConnError C.ConnectionError |
85data DbError = ConnError C.ConnectionError |
75 RelError RelationalError |
86 RelError RelationalError |
76 TransactionRolledBack
87 TransactionRolledBack
@@ -82,22 +93,22 @@ execute expr = do
82 ret <- executeOrErr expr
93 ret <- executeOrErr expr
83 case ret of
94 case ret of
84 Left err -> liftIO $ cancelTransaction (RelError err)
95 Left err -> liftIO $ cancelTransaction (RelError err)
85− Right _ -> pure ()
96+ Right _ -> pure ()
87-- | Run a 'RelationalExpr' query in the 'DB' monad. Relational expressions perform read-only queries against the current database state.
98-- | Run a 'RelationalExpr' query in the 'DB' monad. Relational expressions perform read-only queries against the current database state.
88query :: C.RelationalExpr -> Db Relation
99query :: C.RelationalExpr -> Db Relation
89query expr = do
100query expr = do
90 ret <- queryOrErr expr
101 ret <- queryOrErr expr
91 case ret of
102 case ret of
92− Left err -> liftIO $ cancelTransaction (RelError err)
103+ Left err -> liftIO $ cancelTransaction (RelError err)
93 Right rel -> pure rel
104 Right rel -> pure rel
95-- | Run a 'DatabaseContextExpr' update expression. If there is an error, just return it without cancelling the current transaction.
106-- | Run a 'DatabaseContextExpr' update expression. If there is an error, just return it without cancelling the current transaction.
96executeOrErr :: C.DatabaseContextExpr -> Db (Either RelationalError ())
107executeOrErr :: C.DatabaseContextExpr -> Db (Either RelationalError ())
97executeOrErr expr = Db $ do
108executeOrErr expr = Db $ do
98− (sess, conn) <- ask
109+ (sess, conn) <- ask
99 lift $ C.executeDatabaseContextExpr sess conn expr
110 lift $ C.executeDatabaseContextExpr sess conn expr
101-- | Run a 'RelationalExpr' query expression. If there is an error, just return it without cancelling the transaction.
112-- | Run a 'RelationalExpr' query expression. If there is an error, just return it without cancelling the transaction.
102queryOrErr :: C.RelationalExpr -> Db (Either RelationalError Relation)
113queryOrErr :: C.RelationalExpr -> Db (Either RelationalError Relation)
103queryOrErr expr = Db $ do
114queryOrErr expr = Db $ do