pg-entity-0.0.1.0: src/Database/PostgreSQL/Entity/DBT.hs
{-|
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
, withPool'
, execute
, query
, query_
, queryOne
, QueryNature(..)
) where
import Colourista.IO (cyanMessage, redMessage, yellowMessage)
import Control.Monad.IO.Class
import Control.Monad.Trans.Control (MonadBaseControl)
import Data.Int
import Data.Maybe (listToMaybe)
import Data.Pool (Pool, createPool, withResource)
import Data.Text.Encoding (decodeUtf8)
import Data.Time (NominalDiffTime)
import Data.Vector (Vector)
import qualified Data.Vector as V
import Control.Monad.Catch (Exception, MonadCatch, try)
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
withPool :: (MonadBaseControl IO m)
=> Pool Connection -> PGT.DBT m a -> m a
withPool pool action = withResource pool $ PGT.runDBTSerializable action
-- | Run a DBT action while handling errors as Exceptions.
--
-- This function wraps the DBT actions in a 'try', so that exceptions
-- raised will be converted to the Left branch of the Either.
--
-- @since 0.0.1.0
withPool' :: forall errorType result m
. (Exception errorType, MonadCatch m, MonadBaseControl IO m)
=> Pool Connection
-> PGT.DBT m result
-> m (Either errorType result)
withPool' pool action = try $ withPool pool action
-- | 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 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
result <- PGT.query q params
pure $ listToMaybe result
-- | 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 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
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 -> liftIO $ cyanMessage $ "[SELECT] " <> decodeUtf8 msg
Update -> liftIO $ yellowMessage $ "[UPDATE] " <> decodeUtf8 msg
Insert -> liftIO $ yellowMessage $ "[INSERT] " <> decodeUtf8 msg
Delete -> liftIO $ redMessage $ "[DELETE] " <> decodeUtf8 msg
-- | 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)