hasql/library/Hasql/Query.hs

167 lines
5.4 KiB
Haskell
Raw Normal View History

2015-11-22 10:10:21 +03:00
module Hasql.Query
2015-11-08 21:09:42 +03:00
(
Query(..),
2015-11-22 10:10:21 +03:00
-- * Execution
2015-11-08 21:09:42 +03:00
ResultsError(..),
ResultError(..),
RowError(..),
2015-11-22 10:10:21 +03:00
run,
2015-11-08 21:09:42 +03:00
)
where
import Hasql.Prelude
import qualified Database.PostgreSQL.LibPQ as LibPQ
import qualified Hasql.PreparedStatementRegistry as PreparedStatementRegistry
2015-12-05 09:09:31 +03:00
import qualified Hasql.Decoders.Results as ResultsDecoders
import qualified Hasql.Decoders as Decoders
import qualified Hasql.Encoders.Params as ParamsEncoders
import qualified Hasql.Encoders as Encoders
2015-11-08 21:09:42 +03:00
import qualified Hasql.Settings as Settings
import qualified Hasql.IO as IO
2015-11-22 10:10:21 +03:00
import qualified Hasql.Connection.Impl as Connection
2015-11-08 21:09:42 +03:00
2015-11-21 17:52:25 +03:00
-- |
-- An error of the result-decoder.
2015-11-08 21:09:42 +03:00
data ResultsError =
-- |
-- An error on the client-side,
-- with a message generated by the \"libpq\" library.
-- Usually indicates problems with connection.
2015-11-08 21:18:59 +03:00
ClientError !(Maybe ByteString) |
2015-11-21 17:52:25 +03:00
-- |
-- Decoder error details.
2015-11-08 21:09:42 +03:00
ResultError !ResultError
2015-11-15 12:10:28 +03:00
deriving (Show, Eq)
2015-11-08 21:09:42 +03:00
2015-11-21 17:52:25 +03:00
-- |
-- Decoder error details.
2015-11-08 21:09:42 +03:00
data ResultError =
-- |
2015-11-22 09:30:59 +03:00
-- An error reported by the DB.
-- Consists of the following: Code, message, details, hint.
--
2015-11-22 09:30:59 +03:00
-- * __Code__.
-- The SQLSTATE code for the error.
-- It's recommended to use
-- <http://hackage.haskell.org/package/postgresql-error-codes the "postgresql-error-codes" package>
-- to work with those.
--
2015-11-22 09:30:59 +03:00
-- * __Message__.
-- The primary human-readable error message (typically one line). Always present.
--
2015-11-22 09:30:59 +03:00
-- * __Details__.
-- An optional secondary error message carrying more detail about the problem.
2015-11-08 21:09:42 +03:00
-- Might run to multiple lines.
--
2015-11-22 09:30:59 +03:00
-- * __Hint__.
-- An optional suggestion on what to do about the problem.
-- This is intended to differ from detail in that it offers advice (potentially inappropriate)
2015-11-22 09:30:59 +03:00
-- rather than hard facts.
-- Might run to multiple lines.
2015-11-08 21:18:59 +03:00
ServerError !ByteString !ByteString !(Maybe ByteString) !(Maybe ByteString) |
2015-11-08 21:09:42 +03:00
-- |
-- 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
2015-11-15 12:10:28 +03:00
deriving (Show, Eq)
2015-11-08 21:09:42 +03:00
2015-11-21 17:52:25 +03:00
-- |
-- An error during the decoding of a specific row.
2015-11-08 21:09:42 +03:00
data RowError =
2015-11-21 17:52:25 +03:00
-- |
-- Appears on the attempt to parse more columns than there are in the result.
2015-11-08 21:09:42 +03:00
EndOfInput |
2015-11-21 17:52:25 +03:00
-- |
-- Appears on the attempt to parse a @NULL@ as some value.
2015-11-08 21:09:42 +03:00
UnexpectedNull |
2015-11-21 17:52:25 +03:00
-- |
-- Appears when a wrong value parser is used.
-- Comes with the error details.
2015-11-08 21:09:42 +03:00
ValueError !Text
2015-11-15 12:10:28 +03:00
deriving (Show, Eq)
2015-11-08 21:09:42 +03:00
2015-11-14 13:58:30 +03:00
-- |
2015-11-21 17:55:53 +03:00
-- A specification of a strictly single-statement query, which can be parameterized and prepared.
--
2015-11-21 17:03:37 +03:00
-- Consists of the following:
--
2015-11-21 17:03:37 +03:00
-- * SQL template,
-- * params encoder,
-- * result decoder,
-- * a flag, determining whether it should be prepared.
--
2015-11-21 17:03:37 +03:00
-- The SQL template must be formatted according to Postgres' standard,
2015-12-05 10:47:57 +03:00
-- with any non-ASCII characters of the template encoded using UTF-8.
2015-11-21 17:03:37 +03:00
-- According to the format,
-- parameters must be referred to using the positional notation, as in the following:
-- @$1@, @$2@, @$3@ and etc.
2015-12-05 09:09:31 +03:00
-- Those references must be used to refer to the values of the 'Encoders.Params' encoder.
--
2015-11-21 17:03:37 +03:00
-- Following is an example of the declaration of a prepared statement with its associated codecs.
--
2015-11-21 17:03:37 +03:00
-- @
-- selectSum :: Hasql.'Hasql.Query' (Int64, Int64) Int64
-- selectSum =
-- Hasql.'Hasql.Query' sql encoder decoder True
-- where
-- sql =
-- "select ($1 + $2)"
-- encoder =
2015-12-05 09:09:31 +03:00
-- 'contramap' 'fst' (Hasql.Encoders.'Hasql.Encoders.value' Hasql.Encoders.'Hasql.Encoders.int8') '<>'
-- 'contramap' 'snd' (Hasql.Encoders.'Hasql.Encoders.value' Hasql.Encoders.'Hasql.Encoders.int8')
2015-11-21 17:03:37 +03:00
-- decoder =
2015-12-05 09:09:31 +03:00
-- Hasql.Decoders.'Hasql.Decoders.singleRow' (Hasql.Decoders.'Hasql.Decoders.value' Hasql.Decoders.'Hasql.Decoders.int8')
2015-11-21 17:03:37 +03:00
-- @
--
2015-11-21 17:03:37 +03:00
-- The statement above accepts a product of two parameters of type 'Int64'
2015-12-05 10:47:57 +03:00
-- and produces a single result of type 'Int64'.
--
2015-11-21 17:03:37 +03:00
data Query a b =
2015-12-05 09:09:31 +03:00
Query !ByteString !(Encoders.Params a) !(Decoders.Result b) !Bool
2015-11-21 17:03:37 +03:00
deriving (Functor)
instance Profunctor Query where
2015-11-21 17:54:46 +03:00
{-# INLINE lmap #-}
2015-11-21 17:03:37 +03:00
lmap f (Query p1 p2 p3 p4) =
Query p1 (contramap f p2) p3 p4
2015-11-21 17:54:46 +03:00
{-# INLINE rmap #-}
2015-11-21 17:03:37 +03:00
rmap f (Query p1 p2 p3 p4) =
Query p1 p2 (fmap f p3) p4
2015-11-21 17:54:46 +03:00
{-# INLINE dimap #-}
2015-11-21 17:03:37 +03:00
dimap f1 f2 (Query p1 p2 p3 p4) =
Query p1 (contramap f1 p2) (fmap f2 p3) p4
2015-11-14 13:58:30 +03:00
2015-11-08 21:09:42 +03:00
-- |
2015-11-22 10:10:21 +03:00
-- Execute the query, producing either a deserialization failure or a successful result.
run :: Query a b -> a -> Connection.Connection -> IO (Either ResultsError b)
run (Query template encoder decoder preparable) params (Connection.Connection pqConnectionRef integerDatetimes registry) =
{-# SCC "query" #-}
2015-12-28 10:14:42 +03:00
withMVar pqConnectionRef $ \pqConnection ->
fmap (mapLeft coerceResultsError) $ runEitherT $ do
EitherT $ IO.sendParametricQuery pqConnection integerDatetimes registry template (coerceEncoder encoder) preparable params
EitherT $ IO.getResults pqConnection integerDatetimes (coerceDecoder decoder)
2015-11-08 21:09:42 +03:00
-- |
-- WARNING: We need to take special care that the structure of
2015-12-05 09:09:31 +03:00
-- the "ResultsDecoders.Error" type in the public API is an exact copy of
2015-11-08 21:09:42 +03:00
-- "Error", since we're using coercion.
2015-12-05 09:09:31 +03:00
coerceResultsError :: ResultsDecoders.Error -> ResultsError
2015-11-08 21:09:42 +03:00
coerceResultsError =
unsafeCoerce
2015-12-05 09:09:31 +03:00
coerceDecoder :: Decoders.Result a -> ResultsDecoders.Results a
2015-11-21 13:36:01 +03:00
coerceDecoder =
2015-11-08 21:09:42 +03:00
unsafeCoerce
2015-12-05 09:09:31 +03:00
coerceEncoder :: Encoders.Params a -> ParamsEncoders.Params a
2015-11-21 13:36:01 +03:00
coerceEncoder =
2015-11-08 21:09:42 +03:00
unsafeCoerce