mirror of
https://github.com/nikita-volkov/hasql.git
synced 2025-01-01 15:04:17 +03:00
137 lines
4.6 KiB
Haskell
137 lines
4.6 KiB
Haskell
module Hasql
|
|
(
|
|
-- * Connection
|
|
Connection,
|
|
Settings.Settings(..),
|
|
ConnectionError(..),
|
|
connect,
|
|
disconnect,
|
|
-- * Query
|
|
Query(..),
|
|
ResultsError(..),
|
|
ResultError(..),
|
|
RowError(..),
|
|
query,
|
|
)
|
|
where
|
|
|
|
import Hasql.Prelude
|
|
import qualified Database.PostgreSQL.LibPQ as LibPQ
|
|
import qualified Hasql.PreparedStatementRegistry as PreparedStatementRegistry
|
|
import qualified Hasql.Deserialization.Results as ResultsDeserialization
|
|
import qualified Hasql.Deserialization as Deserialization
|
|
import qualified Hasql.Serialization.Params as ParamsSerialization
|
|
import qualified Hasql.Serialization as Serialization
|
|
import qualified Hasql.Settings as Settings
|
|
import qualified Hasql.IO as IO
|
|
|
|
|
|
-- |
|
|
-- A single connection to the database.
|
|
data Connection =
|
|
Connection !LibPQ.Connection !Bool !PreparedStatementRegistry.PreparedStatementRegistry
|
|
|
|
data ResultsError =
|
|
-- |
|
|
-- An error on the client-side,
|
|
-- with a message generated by the \"libpq\" library.
|
|
-- Usually indicates problems with connection.
|
|
ClientError !(Maybe ByteString) |
|
|
ResultError !ResultError
|
|
deriving (Show, Eq)
|
|
|
|
data ResultError =
|
|
-- |
|
|
-- An error reported by the DB. Code, message, details, hint.
|
|
--
|
|
-- * The SQLSTATE code for the error. The SQLSTATE code identifies the type of error that has occurred;
|
|
-- it can be used by front-end applications to perform specific operations (such as error handling)
|
|
-- in response to a particular database error.
|
|
-- For a list of the possible SQLSTATE codes, see Appendix A.
|
|
-- This field is not localizable, and is always present.
|
|
--
|
|
-- * The primary human-readable error message (typically one line). Always present.
|
|
--
|
|
-- * Detail: an optional secondary error message carrying more detail about the problem.
|
|
-- Might run to multiple lines.
|
|
--
|
|
-- * Hint: an optional suggestion what to do about the problem.
|
|
-- This is intended to differ from detail in that it offers advice (potentially inappropriate)
|
|
-- rather than hard facts. Might run to multiple lines.
|
|
ServerError !ByteString !ByteString !(Maybe ByteString) !(Maybe ByteString) |
|
|
-- |
|
|
-- The database returned an unexpected result.
|
|
-- Indicates an improper statement or a schema mismatch.
|
|
UnexpectedResult !Text |
|
|
-- |
|
|
-- An error of the row reader, preceded by the index of the row.
|
|
RowError !Int !RowError |
|
|
-- |
|
|
-- An unexpected amount of rows.
|
|
UnexpectedAmountOfRows !Int
|
|
deriving (Show, Eq)
|
|
|
|
data RowError =
|
|
EndOfInput |
|
|
UnexpectedNull |
|
|
ValueError !Text
|
|
deriving (Show, Eq)
|
|
|
|
-- |
|
|
-- A connection acquistion error.
|
|
type ConnectionError =
|
|
Maybe ByteString
|
|
|
|
-- |
|
|
-- Acquire a connection using the provided settings.
|
|
connect :: Settings.Settings -> IO (Either ConnectionError Connection)
|
|
connect settings =
|
|
{-# SCC "connect" #-}
|
|
runEitherT $ do
|
|
pqConnection <- lift (IO.acquireConnection settings)
|
|
lift (IO.checkConnectionStatus pqConnection) >>= traverse left
|
|
lift (IO.initConnection pqConnection)
|
|
integerDatetimes <- lift (IO.getIntegerDatetimes pqConnection)
|
|
registry <- lift (IO.acquirePreparedStatementRegistry)
|
|
pure (Connection pqConnection integerDatetimes registry)
|
|
|
|
-- |
|
|
-- Release the connection.
|
|
disconnect :: Connection -> IO ()
|
|
disconnect (Connection pqConnection _ _) =
|
|
LibPQ.finish pqConnection
|
|
|
|
|
|
-- |
|
|
-- A strictly single-statement query, which can be parameterized and prepared.
|
|
--
|
|
-- SQL template, params serializer, result deserializer and a flag, determining whether it should be prepared.
|
|
--
|
|
type Query a b =
|
|
(ByteString, Serialization.Params a, Deserialization.Result b, Bool)
|
|
|
|
-- |
|
|
-- Execute a parametric query, producing either a deserialization failure or a successful result.
|
|
query :: Connection -> Query a b -> a -> IO (Either ResultsError b)
|
|
query (Connection pqConnection integerDatetimes registry) (template, serializer, deserializer, preparable) params =
|
|
{-# SCC "query" #-}
|
|
fmap (mapLeft coerceResultsError) $ runEitherT $ do
|
|
EitherT $ IO.sendParametricQuery pqConnection integerDatetimes registry template (coerceSerializer serializer) preparable params
|
|
EitherT $ IO.getResults pqConnection integerDatetimes (coerceDeserializer deserializer)
|
|
|
|
-- |
|
|
-- WARNING: We need to take special care that the structure of
|
|
-- the "ResultsDeserialization.Error" type in the public API is an exact copy of
|
|
-- "Error", since we're using coercion.
|
|
coerceResultsError :: ResultsDeserialization.Error -> ResultsError
|
|
coerceResultsError =
|
|
unsafeCoerce
|
|
|
|
coerceDeserializer :: Deserialization.Result a -> ResultsDeserialization.Results a
|
|
coerceDeserializer =
|
|
unsafeCoerce
|
|
|
|
coerceSerializer :: Serialization.Params a -> ParamsSerialization.Params a
|
|
coerceSerializer =
|
|
unsafeCoerce
|