-- | This module contains types and information necessary for a SQL database.
-- Database support libraries, like @persistent-postgresql@, will be responsible
-- for constructing these values.
module Database.Persist.SqlBackend
    ( -- * The type and construction
      SqlBackend
    , mkSqlBackend
    , MkSqlBackendArgs(..)
    -- * Utilities

    -- $utilities

    -- ** SqlBackend Getters
    , getEscapedFieldName
    , getEscapedRawName
    , getEscapeRawNameFunction
    , getConnLimitOffset
    , getConnUpsertSql
    -- ** SqlBackend Setters
    , setConnMaxParams
    , setConnRepsertManySql
    , setConnInsertManySql
    , setConnUpsertSql
    , setConnPutManySql
    ) where

import Control.Monad.Reader
import Data.Text (Text)
import Database.Persist.Class.PersistStore (BackendCompatible(..))
import Database.Persist.SqlBackend.Internal
import qualified Database.Persist.SqlBackend.Internal as SqlBackend
       (SqlBackend(..))
import Database.Persist.SqlBackend.Internal.MkSqlBackend as Mk (MkSqlBackendArgs(..))
import Database.Persist.Types.Base
import Database.Persist.Names
import Database.Persist.SqlBackend.Internal.InsertSqlResult
import Data.List.NonEmpty (NonEmpty)

-- $utilities
--
-- The functions exported here are a bit more general than the record accessors.
-- The easiest way to use them is to provide the 'SqlBackend' directly to the
-- function. However, you can also use them in a 'ReaderT' context, and you can
-- even use them with any @backend@ type tht has a @'BackendCompatible'
-- 'SqlBackend' backend@ instance.

-- | This function can be used directly with a 'SqlBackend' to escape
-- a 'FieldNameDB'.
--
-- @
-- let conn :: SqlBackend
-- getEscapedFieldName (FieldNameDB "asdf") conn
-- @
--
-- Alternatively, you can use it in a @'ReaderT' 'SqlBackend'@ context, like
-- 'SqlPersistT':
--
-- @
-- query :: SqlPersistM Text
-- query = do
--     field <- getEscapedFieldName (FieldNameDB "asdf")
--     pure field
-- @
--
-- @since 2.13.0.0
getEscapedFieldName
    :: (BackendCompatible SqlBackend backend, MonadReader backend m)
    => FieldNameDB -> m Text
getEscapedFieldName :: FieldNameDB -> m Text
getEscapedFieldName FieldNameDB
fieldName = do
    FieldNameDB -> Text
func <- (backend -> FieldNameDB -> Text) -> m (FieldNameDB -> Text)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (SqlBackend -> FieldNameDB -> Text
SqlBackend.connEscapeFieldName (SqlBackend -> FieldNameDB -> Text)
-> (backend -> SqlBackend) -> backend -> FieldNameDB -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. backend -> SqlBackend
forall sup sub. BackendCompatible sup sub => sub -> sup
projectBackend)
    Text -> m Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FieldNameDB -> Text
func FieldNameDB
fieldName)

-- | This function can be used directly with a 'SqlBackend' to escape
-- a raw 'Text'.
--
-- @
-- let conn :: SqlBackend
-- getEscapedRawName (FieldNameDB "asdf") conn
-- @
--
-- Alternatively, you can use it in a @'ReaderT' 'SqlBackend'@ context, like
-- 'SqlPersistT':
--
-- @
-- query :: SqlPersistM Text
-- query = do
--     field <- getEscapedRawName (FieldNameDB "asdf")
--     pure field
-- @
--
-- @since 2.13.0.0
getEscapedRawName
    :: (BackendCompatible SqlBackend backend, MonadReader backend m)
    => Text -> m Text
getEscapedRawName :: Text -> m Text
getEscapedRawName Text
name = do
    Text -> Text
func <- m (Text -> Text)
forall backend (m :: * -> *).
(BackendCompatible SqlBackend backend, MonadReader backend m) =>
m (Text -> Text)
getEscapeRawNameFunction
    Text -> m Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Text
func Text
name)

-- | Return the function for escaping a raw name.
--
-- @since 2.13.0.0
getEscapeRawNameFunction
    :: (BackendCompatible SqlBackend backend, MonadReader backend m)
    => m (Text -> Text)
getEscapeRawNameFunction :: m (Text -> Text)
getEscapeRawNameFunction = do
    (backend -> Text -> Text) -> m (Text -> Text)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (SqlBackend -> Text -> Text
SqlBackend.connEscapeRawName (SqlBackend -> Text -> Text)
-> (backend -> SqlBackend) -> backend -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. backend -> SqlBackend
forall sup sub. BackendCompatible sup sub => sub -> sup
projectBackend)

-- | Decorate the given SQL query with the @(LIMIT, OFFSET)@ specified.
--
-- @since 2.13.0.0
getConnLimitOffset
    :: (BackendCompatible SqlBackend backend, MonadReader backend m)
    => (Int, Int)
    -- ^ The @(LIMIT, OFFSET)@ to put on the query.
    -> Text
    -- ^ The SQL query that the LIMIT/OFFSET clause will be attached to.
    -> m Text
getConnLimitOffset :: (Int, Int) -> Text -> m Text
getConnLimitOffset (Int, Int)
limitOffset Text
sql = do
    (Int, Int) -> Text -> Text
func <- (backend -> (Int, Int) -> Text -> Text)
-> m ((Int, Int) -> Text -> Text)
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (SqlBackend -> (Int, Int) -> Text -> Text
SqlBackend.connLimitOffset (SqlBackend -> (Int, Int) -> Text -> Text)
-> (backend -> SqlBackend) -> backend -> (Int, Int) -> Text -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. backend -> SqlBackend
forall sup sub. BackendCompatible sup sub => sub -> sup
projectBackend)
    Text -> m Text
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> m Text) -> Text -> m Text
forall a b. (a -> b) -> a -> b
$ (Int, Int) -> Text -> Text
func (Int, Int)
limitOffset Text
sql

-- | Retrieve the function for generating an upsert statement, if the backend
-- supports it.
--
-- @since 2.13.0.0
getConnUpsertSql
    :: (BackendCompatible SqlBackend backend, MonadReader backend m)
    => m (Maybe (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text))
getConnUpsertSql :: m (Maybe
     (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text))
getConnUpsertSql = do
    (backend
 -> Maybe
      (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text))
-> m (Maybe
        (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text))
forall r (m :: * -> *) a. MonadReader r m => (r -> a) -> m a
asks (SqlBackend
-> Maybe
     (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text)
SqlBackend.connUpsertSql (SqlBackend
 -> Maybe
      (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text))
-> (backend -> SqlBackend)
-> backend
-> Maybe
     (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. backend -> SqlBackend
forall sup sub. BackendCompatible sup sub => sub -> sup
projectBackend)


-- | Set the maximum parameters that may be issued in a given SQL query. This
-- should be used only if the database backend have this limitation.
--
-- @since 2.13.0.0
setConnMaxParams
    :: Int
    -> SqlBackend
    -> SqlBackend
setConnMaxParams :: Int -> SqlBackend -> SqlBackend
setConnMaxParams Int
i SqlBackend
sb =
    SqlBackend
sb { connMaxParams :: Maybe Int
connMaxParams = Int -> Maybe Int
forall a. a -> Maybe a
Just Int
i }

-- | Set the 'connRepsertManySql' field on the 'SqlBackend'. This should only be
-- set by the database backend library. If this is not set, a slow default will
-- be used.
--
-- @since 2.13.0.0
setConnRepsertManySql
    :: (EntityDef -> Int -> Text)
    -> SqlBackend
    -> SqlBackend
setConnRepsertManySql :: (EntityDef -> Int -> Text) -> SqlBackend -> SqlBackend
setConnRepsertManySql EntityDef -> Int -> Text
mkQuery SqlBackend
sb =
    SqlBackend
sb { connRepsertManySql :: Maybe (EntityDef -> Int -> Text)
connRepsertManySql = (EntityDef -> Int -> Text) -> Maybe (EntityDef -> Int -> Text)
forall a. a -> Maybe a
Just EntityDef -> Int -> Text
mkQuery }

-- | Set the 'connInsertManySql' field on the 'SqlBackend'. This should only be
-- used by the database backend library to provide an efficient implementation
-- of a bulk insert function. If this is not set, a slow default will be used.
--
-- @since 2.13.0.0
setConnInsertManySql
    :: (EntityDef -> [[PersistValue]] -> InsertSqlResult)
    -> SqlBackend
    -> SqlBackend
setConnInsertManySql :: (EntityDef -> [[PersistValue]] -> InsertSqlResult)
-> SqlBackend -> SqlBackend
setConnInsertManySql EntityDef -> [[PersistValue]] -> InsertSqlResult
mkQuery SqlBackend
sb =
    SqlBackend
sb { connInsertManySql :: Maybe (EntityDef -> [[PersistValue]] -> InsertSqlResult)
connInsertManySql = (EntityDef -> [[PersistValue]] -> InsertSqlResult)
-> Maybe (EntityDef -> [[PersistValue]] -> InsertSqlResult)
forall a. a -> Maybe a
Just EntityDef -> [[PersistValue]] -> InsertSqlResult
mkQuery }

-- | Set the 'connUpsertSql' field on the 'SqlBackend'. This should only be used
-- by the database backend library to provide an efficient implementation of
-- a bulk insert function. If this is not set, a slow default will be used.
--
-- @since 2.13.0.0
setConnUpsertSql
    :: (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text)
    -> SqlBackend
    -> SqlBackend
setConnUpsertSql :: (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text)
-> SqlBackend -> SqlBackend
setConnUpsertSql EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text
mkQuery SqlBackend
sb =
    SqlBackend
sb { connUpsertSql :: Maybe
  (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text)
connUpsertSql = (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text)
-> Maybe
     (EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text)
forall a. a -> Maybe a
Just EntityDef -> NonEmpty (FieldNameHS, FieldNameDB) -> Text -> Text
mkQuery }

-- | Set the 'connPutManySql field on the 'SqlBackend'. This should only be used
-- by the database backend library to provide an efficient implementation of
-- a bulk insert function. If this is not set, a slow default will be used.
--
-- @since 2.13.0.0
setConnPutManySql
    :: (EntityDef -> Int -> Text)
    -> SqlBackend
    -> SqlBackend
setConnPutManySql :: (EntityDef -> Int -> Text) -> SqlBackend -> SqlBackend
setConnPutManySql  EntityDef -> Int -> Text
mkQuery SqlBackend
sb =
    SqlBackend
sb { connPutManySql :: Maybe (EntityDef -> Int -> Text)
connPutManySql = (EntityDef -> Int -> Text) -> Maybe (EntityDef -> Int -> Text)
forall a. a -> Maybe a
Just EntityDef -> Int -> Text
mkQuery }