module ProjectM36.Client.Simple (
simpleConnectProjectM36,
simpleConnectProjectM36At,
withTransaction,
withTransactionUsing,
execute,
executeOrErr,
query,
queryOrErr,
cancelTransaction,
orCancelTransaction,
rollback,
close,
Atom(..),
AtomType(..),
Db,
DbConn,
DbError(..),
RelationalError(..),
Attribute(..),
C.Atomable(toAtom, fromAtom),
C.ConnectionInfo(..),
C.PersistenceStrategy(..),
C.NotificationCallback,
C.emptyNotificationCallback,
C.DatabaseContextExpr(..),
C.RelationalExprBase(..)
) where
import Control.Exception.Base
import Control.Monad ((<=<))
import Control.Monad.Reader
import ProjectM36.Base
import qualified ProjectM36.Client as C
import ProjectM36.Error
type DbConn = (C.SessionId, C.Connection)
newtype Db a = Db {runDb :: ReaderT DbConn IO a}
deriving (Functor, Applicative, Monad, MonadIO)
newtype TransactionCancelled = TransactionCancelled DbError deriving Show
instance Exception TransactionCancelled
simpleConnectProjectM36At :: HeadName -> C.ConnectionInfo -> IO (Either DbError DbConn)
simpleConnectProjectM36At headName connInfo = do
eConn <- C.connectProjectM36 connInfo
case eConn of
Left err -> pure (Left (ConnError err))
Right conn -> do
eSess <- C.createSessionAtHead conn headName
case eSess of
Left err -> do
C.close conn
pure (Left (RelError err))
Right sess -> pure (Right (sess, conn))
simpleConnectProjectM36 :: C.ConnectionInfo -> IO (Either DbError DbConn)
simpleConnectProjectM36 = simpleConnectProjectM36At "master"
close :: DbConn -> IO ()
close (_ , conn) = C.close conn
withTransaction :: DbConn -> Db a -> IO (Either DbError a)
withTransaction sessconn = withTransactionUsing sessconn UnionMergeStrategy
withTransactionUsing :: DbConn -> MergeStrategy -> Db a -> IO (Either DbError a)
withTransactionUsing (sess, conn) strat dbm = do
eHeadName <- C.headName sess conn
case eHeadName of
Left err -> pure (Left (RelError err))
Right headName -> do
let successFunc = C.autoMergeToHead sess conn strat headName
block = runReaderT (runDb dbm) (sess, conn)
handler :: TransactionCancelled -> IO (Either DbError a)
handler (TransactionCancelled err) = pure (Left err)
handle handler $ do
ret <- C.withTransaction sess conn (block >>= pure . Right) successFunc
case ret of
Left err -> pure (Left (RelError err))
Right val -> pure (Right val)
data DbError = ConnError C.ConnectionError |
RelError RelationalError |
TransactionRolledBack
deriving (Eq, Show)
execute :: C.DatabaseContextExpr -> Db ()
execute = orCancelTransaction <=< executeOrErr
query :: C.RelationalExpr -> Db Relation
query = orCancelTransaction <=< queryOrErr
executeOrErr :: C.DatabaseContextExpr -> Db (Either RelationalError ())
executeOrErr expr = Db $ do
(sess, conn) <- ask
lift $ C.executeDatabaseContextExpr sess conn expr
queryOrErr :: C.RelationalExpr -> Db (Either RelationalError Relation)
queryOrErr expr = Db $ do
(sess, conn) <- ask
lift $ C.executeRelationalExpr sess conn expr
rollback :: Db ()
rollback = cancelTransaction TransactionRolledBack
cancelTransaction :: DbError -> Db a
cancelTransaction err = liftIO $ throwIO (TransactionCancelled err)
orCancelTransaction :: Either RelationalError a -> Db a
orCancelTransaction = either (cancelTransaction . RelError) pure