{-# LANGUAGE ScopedTypeVariables #-}
module Database.Persist.Sql.Run where
import Control.Exception (bracket, mask, onException)
import Control.Monad (liftM)
import Control.Monad.IO.Unlift
import qualified UnliftIO.Exception as UE
import Control.Monad.Logger.CallStack
import Control.Monad.Trans.Reader hiding (local)
import Control.Monad.Trans.Resource
import Data.IORef (readIORef)
import Data.Pool as P
import qualified Data.Map as Map
import qualified Data.Text as T
import System.Timeout (timeout)
import Database.Persist.Class.PersistStore
import Database.Persist.Sql.Types
import Database.Persist.Sql.Types.Internal (IsolationLevel)
import Database.Persist.Sql.Raw
runSqlPool
:: (MonadUnliftIO m, BackendCompatible SqlBackend backend)
=> ReaderT backend m a -> Pool backend -> m a
runSqlPool r pconn = withRunInIO $ \run -> withResource pconn $ run . runSqlConn r
runSqlPoolWithIsolation
:: (MonadUnliftIO m, BackendCompatible SqlBackend backend)
=> ReaderT backend m a -> Pool backend -> IsolationLevel -> m a
runSqlPoolWithIsolation r pconn i = withRunInIO $ \run -> withResource pconn $ run . (\conn -> runSqlConnWithIsolation r conn i)
withResourceTimeout
:: forall a m b. (MonadUnliftIO m)
=> Int
-> Pool a
-> (a -> m b)
-> m (Maybe b)
{-# SPECIALIZE withResourceTimeout :: Int -> Pool a -> (a -> IO b) -> IO (Maybe b) #-}
withResourceTimeout ms pool act = withRunInIO $ \runInIO -> mask $ \restore -> do
mres <- timeout ms $ takeResource pool
case mres of
Nothing -> runInIO $ return (Nothing :: Maybe b)
Just (resource, local) -> do
ret <- restore (runInIO (liftM Just $ act resource)) `onException`
destroyResource pool local resource
putResource local resource
return ret
{-# INLINABLE withResourceTimeout #-}
runSqlConn :: (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> backend -> m a
runSqlConn r conn = withRunInIO $ \runInIO -> mask $ \restore -> do
let conn' = projectBackend conn
getter = getStmtConn conn'
restore $ connBegin conn' getter Nothing
x <- onException
(restore $ runInIO $ runReaderT r conn)
(restore $ connRollback conn' getter)
restore $ connCommit conn' getter
return x
runSqlConnWithIsolation :: (MonadUnliftIO m, BackendCompatible SqlBackend backend) => ReaderT backend m a -> backend -> IsolationLevel -> m a
runSqlConnWithIsolation r conn isolation = withRunInIO $ \runInIO -> mask $ \restore -> do
let conn' = projectBackend conn
getter = getStmtConn conn'
restore $ connBegin conn' getter $ Just isolation
x <- onException
(restore $ runInIO $ runReaderT r conn)
(restore $ connRollback conn' getter)
restore $ connCommit conn' getter
return x
runSqlPersistM
:: (BackendCompatible SqlBackend backend)
=> ReaderT backend (NoLoggingT (ResourceT IO)) a -> backend -> IO a
runSqlPersistM x conn = runResourceT $ runNoLoggingT $ runSqlConn x conn
runSqlPersistMPool
:: (BackendCompatible SqlBackend backend)
=> ReaderT backend (NoLoggingT (ResourceT IO)) a -> Pool backend -> IO a
runSqlPersistMPool x pool = runResourceT $ runNoLoggingT $ runSqlPool x pool
liftSqlPersistMPool
:: (MonadIO m, BackendCompatible SqlBackend backend)
=> ReaderT backend (NoLoggingT (ResourceT IO)) a -> Pool backend -> m a
liftSqlPersistMPool x pool = liftIO (runSqlPersistMPool x pool)
withSqlPool
:: (MonadLogger m, MonadUnliftIO m, BackendCompatible SqlBackend backend)
=> (LogFunc -> IO backend)
-> Int
-> (Pool backend -> m a)
-> m a
withSqlPool mkConn connCount f = withUnliftIO $ \u -> bracket
(unliftIO u $ createSqlPool mkConn connCount)
destroyAllResources
(unliftIO u . f)
createSqlPool
:: forall m backend. (MonadLogger m, MonadUnliftIO m, BackendCompatible SqlBackend backend)
=> (LogFunc -> IO backend)
-> Int
-> m (Pool backend)
createSqlPool mkConn size = do
logFunc <- askLogFunc
let loggedClose :: backend -> IO ()
loggedClose backend = close' backend `UE.catchAny` \e -> runLoggingT
(logError $ T.pack $ "Error closing database connection in pool: " ++ show e)
logFunc
liftIO $ createPool (mkConn logFunc) loggedClose 1 20 size
askLogFunc :: forall m. (MonadUnliftIO m, MonadLogger m) => m LogFunc
askLogFunc = withRunInIO $ \run ->
return $ \a b c d -> run (monadLoggerLog a b c d)
withSqlConn
:: (MonadUnliftIO m, MonadLogger m, BackendCompatible SqlBackend backend)
=> (LogFunc -> IO backend) -> (backend -> m a) -> m a
withSqlConn open f = do
logFunc <- askLogFunc
withRunInIO $ \run -> bracket
(open logFunc)
close'
(run . f)
close' :: (BackendCompatible SqlBackend backend) => backend -> IO ()
close' conn = do
readIORef (connStmtMap $ projectBackend conn) >>= mapM_ stmtFinalize . Map.elems
connClose $ projectBackend conn