{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RankNTypes #-}
module Database.Persist.Sql.Types.Internal
( HasPersistBackend (..)
, IsPersistBackend (..)
, SqlReadBackend (..)
, SqlWriteBackend (..)
, readToUnknown
, readToWrite
, writeToUnknown
, LogFunc
, InsertSqlResult (..)
, Statement (..)
, IsolationLevel (..)
, makeIsolationLevelStatement
, SqlBackend (..)
, SqlBackendCanRead
, SqlBackendCanWrite
, SqlReadT
, SqlWriteT
, IsSqlBackend
) where
import Data.List.NonEmpty (NonEmpty(..))
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Logger (LogSource, LogLevel)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.Reader (ReaderT, runReaderT, ask)
import Data.Acquire (Acquire)
import Data.Conduit (ConduitM)
import Data.Int (Int64)
import Data.IORef (IORef)
import Data.Map (Map)
import Data.Monoid ((<>))
import Data.String (IsString)
import Data.Text (Text)
import Data.Typeable (Typeable)
import Language.Haskell.TH.Syntax (Loc)
import System.Log.FastLogger (LogStr)
import Database.Persist.Class
( HasPersistBackend (..)
, PersistQueryRead, PersistQueryWrite
, PersistStoreRead, PersistStoreWrite
, PersistUniqueRead, PersistUniqueWrite
, BackendCompatible(..)
)
import Database.Persist.Class.PersistStore (IsPersistBackend (..))
import Database.Persist.Types
type LogFunc = Loc -> LogSource -> LogLevel -> LogStr -> IO ()
data InsertSqlResult = ISRSingle Text
| ISRInsertGet Text Text
| ISRManyKeys Text [PersistValue]
data Statement = Statement
{ stmtFinalize :: IO ()
, stmtReset :: IO ()
, stmtExecute :: [PersistValue] -> IO Int64
, stmtQuery :: forall m. MonadIO m
=> [PersistValue]
-> Acquire (ConduitM () [PersistValue] m ())
}
data IsolationLevel = ReadUncommitted
| ReadCommitted
| RepeatableRead
| Serializable
deriving (Show, Eq, Enum, Ord, Bounded)
makeIsolationLevelStatement :: (Monoid s, IsString s) => IsolationLevel -> s
makeIsolationLevelStatement l = "SET TRANSACTION ISOLATION LEVEL " <> case l of
ReadUncommitted -> "READ UNCOMMITTED"
ReadCommitted -> "READ COMMITTED"
RepeatableRead -> "REPEATABLE READ"
Serializable -> "SERIALIZABLE"
data SqlBackend = SqlBackend
{ connPrepare :: Text -> IO Statement
, connInsertSql :: EntityDef -> [PersistValue] -> InsertSqlResult
, connInsertManySql :: Maybe (EntityDef -> [[PersistValue]] -> InsertSqlResult)
, connUpsertSql :: Maybe (EntityDef -> NonEmpty UniqueDef -> Text -> Text)
, connPutManySql :: Maybe (EntityDef -> Int -> Text)
, connStmtMap :: IORef (Map Text Statement)
, connClose :: IO ()
, connMigrateSql
:: [EntityDef]
-> (Text -> IO Statement)
-> EntityDef
-> IO (Either [Text] [(Bool, Text)])
, connBegin :: (Text -> IO Statement) -> Maybe IsolationLevel -> IO ()
, connCommit :: (Text -> IO Statement) -> IO ()
, connRollback :: (Text -> IO Statement) -> IO ()
, connEscapeName :: DBName -> Text
, connNoLimit :: Text
, connRDBMS :: Text
, connLimitOffset :: (Int,Int) -> Bool -> Text -> Text
, connLogFunc :: LogFunc
, connMaxParams :: Maybe Int
, connRepsertManySql :: Maybe (EntityDef -> Int -> Text)
}
deriving Typeable
instance HasPersistBackend SqlBackend where
type BaseBackend SqlBackend = SqlBackend
persistBackend = id
instance IsPersistBackend SqlBackend where
mkPersistBackend = id
newtype SqlReadBackend = SqlReadBackend { unSqlReadBackend :: SqlBackend } deriving Typeable
instance HasPersistBackend SqlReadBackend where
type BaseBackend SqlReadBackend = SqlBackend
persistBackend = unSqlReadBackend
instance IsPersistBackend SqlReadBackend where
mkPersistBackend = SqlReadBackend
newtype SqlWriteBackend = SqlWriteBackend { unSqlWriteBackend :: SqlBackend } deriving Typeable
instance HasPersistBackend SqlWriteBackend where
type BaseBackend SqlWriteBackend = SqlBackend
persistBackend = unSqlWriteBackend
instance IsPersistBackend SqlWriteBackend where
mkPersistBackend = SqlWriteBackend
writeToUnknown :: Monad m => ReaderT SqlWriteBackend m a -> ReaderT SqlBackend m a
writeToUnknown ma = do
unknown <- ask
lift . runReaderT ma $ SqlWriteBackend unknown
readToWrite :: Monad m => ReaderT SqlReadBackend m a -> ReaderT SqlWriteBackend m a
readToWrite ma = do
write <- ask
lift . runReaderT ma . SqlReadBackend $ unSqlWriteBackend write
readToUnknown :: Monad m => ReaderT SqlReadBackend m a -> ReaderT SqlBackend m a
readToUnknown ma = do
unknown <- ask
lift . runReaderT ma $ SqlReadBackend unknown
type SqlBackendCanRead backend =
( BackendCompatible SqlBackend backend
, PersistQueryRead backend, PersistStoreRead backend, PersistUniqueRead backend
)
type SqlBackendCanWrite backend =
( SqlBackendCanRead backend
, PersistQueryWrite backend, PersistStoreWrite backend, PersistUniqueWrite backend
)
type SqlReadT m a = forall backend. (SqlBackendCanRead backend) => ReaderT backend m a
type SqlWriteT m a = forall backend. (SqlBackendCanWrite backend) => ReaderT backend m a
type IsSqlBackend backend = (IsPersistBackend backend, BaseBackend backend ~ SqlBackend)