pg-entity-0.0.4.1: src/Database/PostgreSQL/Entity/DBT.hs
{-# LANGUAGE CPP #-}
{-|
Module : Database.PostgreSQL.Entity.DBT
Copyright : © Clément Delafargue, 2018
Théophile Choutri, 2021
License : MIT
Maintainer : theophile@choutri.eu
Stability : stable
The 'Database.PostgreSQL.Transact.DBT' plumbing module to handle database queries and pools
-}
module Database.PostgreSQL.Entity.DBT
( mkPool
, withPool
, execute
, executeMany
, query
, query_
, queryOne
, queryOne_
, QueryNature (..)
)
where
#ifdef PROD
#else
import Colourista (cyan, red, yellow, formatWith)
import Data.ByteString (ByteString)
import qualified Database.PostgreSQL.Simple as Simple
import System.IO (stdout)
import qualified Data.ByteString.Char8 as BS
#endif
import Control.Monad.IO.Class
#if MIN_VERSION_resource_pool(0,3,0)
#else
import Control.Monad.Trans.Control
#endif
import Data.Int
import Data.Maybe (listToMaybe)
import Data.Pool (Pool, createPool, withResource)
import Data.Time (NominalDiffTime)
import Data.Vector (Vector)
import qualified Data.Vector as V
import Database.PostgreSQL.Simple as PG (ConnectInfo, Connection, FromRow, Query, ToRow, close, connect)
import qualified Database.PostgreSQL.Transact as PGT
{-| Create a Pool Connection with the appropriate parameters
@since 0.0.1.0
-}
mkPool
:: ConnectInfo -- Database access information
-> Int -- Number of sub-pools
-> NominalDiffTime -- Allowed timeout
-> Int -- Number of connections
-> IO (Pool Connection)
mkPool connectInfo subPools timeout connections =
createPool (connect connectInfo) close subPools timeout connections
{-| Run a DBT action with no explicit error handling.
This functions is suited for using 'MonadError' error handling.
=== __Example__
> let e1 = E 1 True True
> result <- runExceptT @EntityError $ do
> withPool pool $ insertEntity e1
> withPool pool $ markForProcessing 1
> case result of
> Left err -> print err
> Right _ -> putStrLn "Everything went well"
See the code in the @example/@ directory on GitHub
@since 0.0.1.0
-}
#if MIN_VERSION_resource_pool(0,3,0)
withPool :: (MonadIO m) => Pool Connection -> PGT.DBT IO a -> m a
withPool pool action = liftIO $ withResource pool (\conn -> PGT.runDBTSerializable action conn)
#else
withPool :: (MonadBaseControl IO m) => Pool Connection -> PGT.DBT m a -> m a
withPool pool action = withResource pool (\conn -> PGT.runDBTSerializable action conn)
#endif
{-| Query wrapper that returns a 'Vector' of results
@since 0.0.1.0
-}
query
:: (ToRow params, FromRow result, MonadIO m)
=> QueryNature
-> Query
-> params
-> PGT.DBT m (Vector result)
query queryNature q params = do
logQueryFormat queryNature q params
V.fromList <$> PGT.query q params
{-| Query wrapper that returns a 'Vector' of results and does not take an argument
@since 0.0.1.0
-}
query_
:: (FromRow result, MonadIO m)
=> QueryNature
-> Query
-> PGT.DBT m (Vector result)
query_ queryNature q = do
logQueryFormat queryNature q ()
V.fromList <$> PGT.query_ q
{-| Query wrapper that returns one result.
@since 0.0.1.0
-}
queryOne
:: (ToRow params, FromRow result, MonadIO m)
=> QueryNature
-> Query
-> params
-> PGT.DBT m (Maybe result)
queryOne queryNature q params = do
logQueryFormat queryNature q params
listToMaybe <$> PGT.query q params
--
{-| Query wrapper that returns one result and does not take an argument
@since 0.0.2.0
-}
queryOne_
:: (FromRow result, MonadIO m)
=> QueryNature
-> Query
-> PGT.DBT m (Maybe result)
queryOne_ queryNature q = do
logQueryFormat queryNature q ()
listToMaybe <$> PGT.query_ q
{-| Query wrapper for SQL statements which do not return.
@since 0.0.1.0
-}
execute
:: (ToRow params, MonadIO m)
=> QueryNature
-> Query
-> params
-> PGT.DBT m Int64
execute queryNature q params = do
logQueryFormat queryNature q params
PGT.execute q params
{-| Query wrapper for SQL statements that operate on multiple rows which do not return.
@since 0.0.2.0
-}
executeMany
:: (ToRow params, MonadIO m)
=> QueryNature
-> Query
-> [params]
-> PGT.DBT m Int64
executeMany queryNature q params = do
logQueryFormatMany queryNature q params
PGT.executeMany q params
#ifndef PROD
displayColoured :: (MonadIO m) => ByteString -> ByteString -> PGT.DBT m ()
displayColoured colour text = liftIO $ BS.hPutStrLn stdout (formatWith [colour] text)
#endif
#ifdef PROD
logQueryFormat :: (Monad m) => QueryNature -> Query -> params -> PGT.DBT m ()
logQueryFormat _ _ _ = pure ()
#else
logQueryFormat :: (ToRow params, MonadIO m)
=> QueryNature -> Query -> params -> PGT.DBT m ()
logQueryFormat queryNature q params = do
msg <- PGT.formatQuery q params
case queryNature of
Select -> displayColoured cyan msg
Update -> displayColoured yellow msg
Insert -> displayColoured yellow msg
Delete -> displayColoured red msg
#endif
#ifdef PROD
logQueryFormatMany :: (Monad m) => QueryNature -> Query -> [params] -> PGT.DBT m ()
logQueryFormatMany _ _ _ = pure ()
#else
logQueryFormatMany :: (ToRow params, MonadIO m) => QueryNature -> Query -> [params] -> PGT.DBT m ()
logQueryFormatMany queryNature q params = do
msg <- formatMany q params
case queryNature of
Select -> displayColoured cyan msg
Update -> displayColoured yellow msg
Insert -> displayColoured yellow msg
Delete -> displayColoured red msg
formatMany :: (ToRow q, MonadIO m) => Query -> [q] -> PGT.DBT m ByteString
formatMany q xs = PGT.getConnection >>= \conn -> liftIO $ Simple.formatMany conn q xs
#endif
{-| This sum type is given to the 'query', 'queryOne' and 'execute' functions to help
with logging.
@since 0.0.1.0
-}
data QueryNature = Select | Insert | Update | Delete deriving (Eq, Show)