opaleye-trans-0.4.2: src/Opaleye/Trans/Exception.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Opaleye.Trans.Exception
( OpaleyeT (..)
, runOpaleyeT
, -- * Transactions
Transaction
, transaction
, run
, -- * Queries
query
, queryFirst
, -- * Inserts
insert
, insertMany
, insertReturning
, insertReturningFirst
, insertManyReturning
, -- * Updates
update
, updateReturning
, updateReturningFirst
, -- * Deletes
delete
, -- * Exceptions
withExceptOpaleye
, withExceptTrans
, -- * Utilities
withError
, withoutError
, liftError
, withTrans
, maybeError
, -- * Reexports
liftIO
, MonadIO
, ask
, Int64
, throwError
, catchError
) where
import Control.Exception (catch, throw)
import Control.Monad.Except (ExceptT (..), MonadError,
catchError, runExceptT,
throwError, withExceptT)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Reader (MonadReader (..))
import Control.Monad.Trans (MonadTrans (..))
import Control.Monad.Catch (MonadCatch, MonadThrow)
import Data.Profunctor.Product.Default (Default)
import Database.PostgreSQL.Simple (Connection)
import qualified Database.PostgreSQL.Simple as PSQL
import GHC.Int (Int64)
import Opaleye
import qualified Opaleye.Trans as T
newtype OpaleyeT e m a = OpaleyeT { unOpaleyeT :: ExceptT e (T.OpaleyeT m) a }
deriving ( Functor, Applicative, Monad, MonadIO, MonadReader Connection
, MonadError e, MonadCatch, MonadThrow
)
instance MonadTrans (OpaleyeT e) where
lift = OpaleyeT . lift . lift
withOpaleye :: Monad m => T.OpaleyeT m a -> OpaleyeT e m a
withOpaleye = OpaleyeT . lift
-- | Given a 'Connection', run an 'OpaleyeT'
runOpaleyeT :: PSQL.Connection -> OpaleyeT e m a -> m (Either e a)
runOpaleyeT c = T.runOpaleyeT c . runExceptT . unOpaleyeT
withExceptOpaleye :: Functor m => (e -> e') -> OpaleyeT e m a -> OpaleyeT e' m a
withExceptOpaleye f = OpaleyeT . withExceptT f . unOpaleyeT
-- | Just like 'T.Transaction' only with exception handling
newtype Transaction e a = Transaction { unTransaction :: ExceptT e T.Transaction a }
deriving (Functor, Applicative, Monad, MonadReader Connection, MonadError e)
withExceptTrans :: (e -> e') -> Transaction e a -> Transaction e' a
withExceptTrans f = Transaction . withExceptT f . unTransaction
withError :: Monad m => T.OpaleyeT m (Either e a) -> OpaleyeT e m a
withError f = withOpaleye f >>= either throwError return
withoutError :: Monad m => OpaleyeT e m a -> T.OpaleyeT m (Either e a)
withoutError = runExceptT . unOpaleyeT
liftError :: Monad m
=> (T.Transaction (Either e a) -> T.OpaleyeT m (Either r b))
-> Transaction e a -> OpaleyeT r m b
liftError f = withError . f . runExceptT . unTransaction
-- | Run a postgresql transaction in the 'OpaleyeT' monad
transaction :: MonadIO m => Transaction e a -> OpaleyeT e m a
transaction = liftError T.transaction
-- | Execute a query without a literal transaction
run :: MonadIO m => Transaction e a -> OpaleyeT e m a
run = liftError T.run
withTrans :: T.Transaction a -> Transaction e a
withTrans = Transaction . lift
-- | Execute a 'Query'. See 'runQuery'.
query :: Default QueryRunner a b => Query a -> Transaction e [b]
query = withTrans . T.query
maybeError :: T.Transaction (Maybe b) -> e -> Transaction e b
maybeError f e = withTrans f >>= maybe (throwError e) return
-- | Retrieve the first result from a 'Query'. Similar to @listToMaybe <$> runQuery@.
queryFirst :: Default QueryRunner a b => e -> Query a -> Transaction e b
queryFirst e q = maybeError (T.queryFirst q) e
-- | Insert into a 'Table'. See 'runInsert'.
insert :: Table w r -> w -> Transaction e Int64
insert t = withTrans . T.insert t
-- | Insert many records into a 'Table'. See 'runInsertMany'.
insertMany :: Table w r -> [w] -> Transaction e Int64
insertMany t = withTrans . T.insertMany t
-- | Insert a record into a 'Table' with a return value. See 'runInsertReturning'.
insertReturning :: Default QueryRunner a b => Table w r -> (r -> a) -> w -> Transaction e [b]
insertReturning t ret = withTrans . T.insertReturning t ret
-- | Insert a record into a 'Table' with a return value. Retrieve only the first result.
-- Similar to @'listToMaybe' '<$>' 'insertReturning'@
insertReturningFirst :: Default QueryRunner a b => e -> Table w r -> (r -> a) -> w -> Transaction e b
insertReturningFirst e t ret w = maybeError (T.insertReturningFirst t ret w) e
-- | Insert many records into a 'Table' with a return value for each record.
--
-- Maybe not worth defining. This almost certainly does the wrong thing.
insertManyReturning :: Default QueryRunner a b => Table w r -> [w] -> (r -> a) -> Transaction e [b]
insertManyReturning t ws = withTrans . T.insertManyReturning t ws
-- | Update items in a 'Table' where the predicate is true. See 'runUpdate'.
update :: Table w r -> (r -> w) -> (r -> Column PGBool) -> Transaction e Int64
update t r2w = withTrans . T.update t r2w
-- | Update items in a 'Table' with a return value. See 'runUpdateReturning'.
updateReturning :: Default QueryRunner a b
=> Table w r
-> (r -> w)
-> (r -> Column PGBool)
-> (r -> a)
-> Transaction e [b]
updateReturning t r2w p = withTrans . T.updateReturning t r2w p
-- | Update items in a 'Table' with a return value. Similar to @'listToMaybe' '<$>' 'updateReturning'@.
updateReturningFirst :: Default QueryRunner a b
=> e
-> Table w r
-> (r -> w)
-> (r -> Column PGBool)
-> (r -> a)
-> Transaction e b
updateReturningFirst e t r2w p r2r = maybeError (T.updateReturningFirst t r2w p r2r) e
-- | Delete items in a 'Table' that satisfy some boolean predicate. See 'runDelete'.
delete :: Table a b -> (b -> Column PGBool) -> Transaction e Int64
delete t = withTrans . T.delete t