-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Fast PostgreSQL driver with a flexible mapping API -- -- Root of the "hasql" ecosystem. This library provides connection -- management, execution of queries and mapping of parameters and -- results. Extended functionality such as pooling, transactions and -- compile-time checking is provided by extension libraries. For more -- details and tutorials see the readme. -- -- The API comes free from all kinds of exceptions. All error-reporting -- is explicit and is presented using the Either type. -- -- "hasql" requires you to have the "libpq" C-library installed to -- compile. Starting from version 1.7 of "hasql" it requires "libpq" of -- at least version 14. "libpq" comes distributed with PostgreSQL, so -- typically all you need is just to install the latest PostgreSQL -- distro. -- -- Despite the mentioned requirements for "libpq" "hasql" is compatible -- with a wide range of PostgreSQL servers with tests having been -- conducted starting from version 8.3. @package hasql @version 1.9 -- | A DSL for declaration of statement parameter encoders. -- -- For compactness of names all the types defined here imply being an -- encoder. E.g., the Array type is an encoder of arrays, -- not the data-structure itself. module Hasql.Encoders -- | Encoder of some representation of a parameters product. -- -- Has instances of Contravariant, Divisible and -- Monoid, which you can use to compose multiple parameters -- together. E.g., -- --
-- someParamsEncoder :: Params (Int64, Maybe Text) -- someParamsEncoder = -- (fst >$< param (nonNullable int8)) <> -- (snd >$< param (nullable text)) ---- -- As a general solution for tuples of any arity, instead of fst -- and snd, consider the functions of the contrazip -- family from the "contravariant-extras" package. E.g., here's how you -- can achieve the same as the above: -- --
-- someParamsEncoder :: Params (Int64, Maybe Text) -- someParamsEncoder = -- contrazip2 (param (nonNullable int8)) (param (nullable text)) ---- -- Here's how you can implement encoders for custom composite types: -- --
-- data Person = Person { name :: Text, gender :: Gender, age :: Int }
--
-- data Gender = Male | Female
--
-- personParams :: Params Person
-- personParams =
-- (name >$< param (nonNullable text)) <>
-- (gender >$< param (nonNullable genderValue)) <>
-- (fromIntegral . age >$< param (nonNullable int8))
--
-- genderValue :: Value Gender
-- genderValue = enum genderText text where
-- genderText gender = case gender of
-- Male -> "male"
-- Female -> "female"
--
data Params a
-- | No parameters. Same as mempty and conquered.
noParams :: Params ()
-- | Lift a single parameter encoder, with its nullability specified,
-- associating it with a single placeholder.
param :: NullableOrNot Value a -> Params a
-- | Extensional specification of nullability over a generic encoder.
data NullableOrNot (encoder :: Type -> Type) a
-- | Specify that an encoder produces a non-nullable value.
nonNullable :: encoder a -> NullableOrNot encoder a
-- | Specify that an encoder produces a nullable value.
nullable :: encoder a -> NullableOrNot encoder (Maybe a)
-- | Value encoder.
data Value a
-- | Encoder of BOOL values.
bool :: Value Bool
-- | Encoder of INT2 values.
int2 :: Value Int16
-- | Encoder of INT4 values.
int4 :: Value Int32
-- | Encoder of INT8 values.
int8 :: Value Int64
-- | Encoder of FLOAT4 values.
float4 :: Value Float
-- | Encoder of FLOAT8 values.
float8 :: Value Double
-- | Encoder of NUMERIC values.
numeric :: Value Scientific
-- | Encoder of CHAR values.
--
-- Note that it supports Unicode values and identifies itself under the
-- TEXT OID because of that.
char :: Value Char
-- | Encoder of TEXT values.
text :: Value Text
-- | Encoder of BYTEA values.
bytea :: Value ByteString
-- | Encoder of DATE values.
date :: Value Day
-- | Encoder of TIMESTAMP values.
timestamp :: Value LocalTime
-- | Encoder of TIMESTAMPTZ values.
timestamptz :: Value UTCTime
-- | Encoder of TIME values.
time :: Value TimeOfDay
-- | Encoder of TIMETZ values.
timetz :: Value (TimeOfDay, TimeZone)
-- | Encoder of INTERVAL values.
interval :: Value DiffTime
-- | Encoder of UUID values.
uuid :: Value UUID
-- | Encoder of INET values.
inet :: Value IPRange
-- | Encoder of JSON values from JSON AST.
json :: Value Value
-- | Encoder of JSON values from raw JSON.
jsonBytes :: Value ByteString
-- | Encoder of JSON values from raw JSON as lazy ByteString.
jsonLazyBytes :: Value ByteString
-- | Encoder of JSONB values from JSON AST.
jsonb :: Value Value
-- | Encoder of JSONB values from raw JSON.
jsonbBytes :: Value ByteString
-- | Encoder of JSONB values from raw JSON as lazy ByteString.
jsonbLazyBytes :: Value ByteString
-- | Encoder of NAME values.
name :: Value Text
-- | Encoder of OID values.
oid :: Value Int32
-- | Given a function, which maps a value into a textual enum label used on
-- the DB side, produces an encoder of that value.
enum :: (a -> Text) -> Value a
-- | Variation of enum with unknown OID. This function does not
-- identify the type to Postgres, so Postgres must be able to derive the
-- type from context. When you find yourself in such situation just
-- provide an explicit type in the query using the :: operator.
unknownEnum :: (a -> Text) -> Value a
-- | Identifies the value with the PostgreSQL's "unknown" type, thus
-- leaving it up to Postgres to infer the actual type of the value.
--
-- The value transimitted is any value encoded in the Postgres' Text data
-- format. For reference, see the Formats and Format Codes section
-- of the Postgres' documentation.
--
-- Warning: Do not use this as part of composite encoders like
-- array since it is the only encoder that doesn't use the binary
-- format.
unknown :: Value ByteString
-- | Lift an array encoder into a value encoder.
array :: Array a -> Value a
-- | Lift a value encoder of element into a unidimensional array encoder of
-- a foldable value.
--
-- This function is merely a shortcut to the following expression:
--
-- -- (array . dimension foldl' . element) ---- -- You can use it like this: -- --
-- vectorOfInts :: Value (Vector Int64) -- vectorOfInts = foldableArray (nonNullable int8) ---- -- Please notice that in case of multidimensional arrays nesting -- foldableArray encoder won't work. You have to explicitly -- construct the array encoder using array. foldableArray :: Foldable foldable => NullableOrNot Value element -> Value (foldable element) -- | Lift a composite encoder into a value encoder. composite :: Composite a -> Value a -- | Generic array encoder. -- -- Here's an example of its usage: -- --
-- someParamsEncoder :: Params [[Int64]] -- someParamsEncoder = param (nonNullable (array (dimension foldl' (dimension foldl' (element (nonNullable int8)))))) ---- -- Please note that the PostgreSQL IN keyword does not accept an -- array, but rather a syntactical list of values, thus this encoder is -- not suited for that. Use a value = ANY($1) condition instead. data Array a -- | Lifts a Value encoder into an Array encoder. element :: NullableOrNot Value a -> Array a -- | Encoder of an array dimension, which thus provides support for -- multidimensional arrays. -- -- Accepts: -- --
-- x :: Row (Maybe Int64, Text, TimeOfDay) -- x = (,,) <$> (column . nullable) int8 <*> (column . nonNullable) text <*> (column . nonNullable) time --data Row a -- | Lift an individual value decoder to a composable row decoder. column :: NullableOrNot Value a -> Row a -- | Extensional specification of nullability over a generic decoder. data NullableOrNot (decoder :: Type -> Type) a -- | Specify that a decoder produces a non-nullable value. nonNullable :: decoder a -> NullableOrNot decoder a -- | Specify that a decoder produces a nullable value. nullable :: decoder a -> NullableOrNot decoder (Maybe a) -- | Decoder of a value. data Value a -- | Decoder of the BOOL values. bool :: Value Bool -- | Decoder of the INT2 values. int2 :: Value Int16 -- | Decoder of the INT4 values. int4 :: Value Int32 -- | Decoder of the INT8 values. int8 :: Value Int64 -- | Decoder of the FLOAT4 values. float4 :: Value Float -- | Decoder of the FLOAT8 values. float8 :: Value Double -- | Decoder of the NUMERIC values. numeric :: Value Scientific -- | Decoder of the CHAR values. Note that it supports Unicode -- values. char :: Value Char -- | Decoder of the TEXT values. text :: Value Text -- | Decoder of the BYTEA values. bytea :: Value ByteString -- | Decoder of the DATE values. date :: Value Day -- | Decoder of the TIMESTAMP values. timestamp :: Value LocalTime -- | Decoder of the TIMESTAMPTZ values. -- -- NOTICE -- -- Postgres does not store the timezone information of -- TIMESTAMPTZ. Instead it stores a UTC value and performs -- silent conversions to the currently set timezone, when dealt with in -- the text format. However this library bypasses the silent conversions -- and communicates with Postgres using the UTC values directly. timestamptz :: Value UTCTime -- | Decoder of the TIME values. time :: Value TimeOfDay -- | Decoder of the TIMETZ values. -- -- Unlike in case of TIMESTAMPTZ, Postgres does store the -- timezone information for TIMETZ. However the Haskell's "time" -- library does not contain any composite type, that fits the task, so we -- use a pair of TimeOfDay and TimeZone to represent a -- value on the Haskell's side. timetz :: Value (TimeOfDay, TimeZone) -- | Decoder of the INTERVAL values. interval :: Value DiffTime -- | Decoder of the UUID values. uuid :: Value UUID -- | Decoder of the INET values. inet :: Value IPRange -- | Decoder of the JSON values into a JSON AST. json :: Value Value -- | Decoder of the JSON values into a raw JSON ByteString. jsonBytes :: (ByteString -> Either Text a) -> Value a -- | Decoder of the JSONB values into a JSON AST. jsonb :: Value Value -- | Decoder of the JSONB values into a raw JSON -- ByteString. jsonbBytes :: (ByteString -> Either Text a) -> Value a -- | Lift an Array decoder to a Value decoder. array :: Array a -> Value a -- | Lift a value decoder of element into a unidimensional array decoder -- producing a list. -- -- This function is merely a shortcut to the following expression: -- --
-- (array . dimension Control.Monad.replicateM . element) ---- -- Please notice that in case of multidimensional arrays nesting -- listArray decoder won't work. You have to explicitly construct -- the array decoder using array. listArray :: NullableOrNot Value element -> Value [element] -- | Lift a value decoder of element into a unidimensional array decoder -- producing a generic vector. -- -- This function is merely a shortcut to the following expression: -- --
-- (array . dimension Data.Vector.Generic.replicateM . element) ---- -- Please notice that in case of multidimensional arrays nesting -- vectorArray decoder won't work. You have to explicitly -- construct the array decoder using array. vectorArray :: Vector vector element => NullableOrNot Value element -> Value (vector element) -- | Lift a Composite decoder to a Value decoder. composite :: Composite a -> Value a -- | A generic decoder of HSTORE values. -- -- Here's how you can use it to construct a specific value: -- --
-- x :: Value [(Text, Maybe Text)] -- x = hstore replicateM --hstore :: (forall (m :: Type -> Type). Monad m => Int -> m (Text, Maybe Text) -> m a) -> Value a -- | Given a partial mapping from text to value, produces a decoder of that -- value. enum :: (Text -> Maybe a) -> Value a -- | Lift a custom value decoder function to a Value decoder. custom :: (Bool -> ByteString -> Either Text a) -> Value a -- | Refine a value decoder, lifting the possible error to the session -- level. refine :: (a -> Either Text b) -> Value a -> Value b -- | A generic array decoder. -- -- Here's how you can use it to produce a specific array value decoder: -- --
-- x :: Value [[Text]] -- x = array (dimension replicateM (dimension replicateM (element (nonNullable text)))) --data Array a -- | A function for parsing a dimension of an array. Provides support for -- multi-dimensional arrays. -- -- Accepts: -- --
-- selectSum :: Statement (Int64, Int64) Int64 -- selectSum = -- Statement sql encoder decoder True -- where -- sql = -- "select ($1 + $2)" -- encoder = -- (fst >$< Encoders.param (Encoders.nonNullable Encoders.int8)) <> -- (snd >$< Encoders.param (Encoders.nonNullable Encoders.int8)) -- decoder = -- Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.int8)) ---- -- The statement above accepts a product of two parameters of type -- Int64 and produces a single result of type Int64. data Statement params result Statement :: ByteString -> Params params -> Result result -> Bool -> Statement params result -- | Refine the result of a statement, causing the running session to fail -- with the UnexpectedResult error in case of a refinement -- failure. -- -- This function is especially useful for refining the results of -- statements produced with the "hasql-th" library. refineResult :: (a -> Either Text b) -> Statement params a -> Statement params b instance GHC.Internal.Base.Functor (Hasql.Statement.Statement params) instance Data.Profunctor.Unsafe.Profunctor Hasql.Statement.Statement module Hasql.Session -- | A batch of actions to be executed in the context of a database -- connection. data Session a -- | Possibly a multi-statement query, which however cannot be -- parameterized or prepared, nor can any results of it be collected. sql :: ByteString -> Session () -- | Execute a statement by providing parameters to it. statement :: params -> Statement params result -> Session result -- | Execute a pipeline. pipeline :: Pipeline result -> Session result -- | Executes a bunch of commands on the provided connection. run :: Session a -> Connection -> IO (Either SessionError a) -- | Error during execution of a session. data SessionError -- | Error during the execution of a query. Comes packed with the query -- template and a textual representation of the provided params. QueryError :: ByteString -> [Text] -> CommandError -> SessionError -- | Error during the execution of a pipeline. PipelineError :: CommandError -> SessionError -- | An error of some command in the session. data CommandError -- | An error on the client-side, with a message generated by the "libpq" -- library. Usually indicates problems with connection. ClientError :: Maybe ByteString -> CommandError -- | Some error with a command result. ResultError :: ResultError -> CommandError -- | An error with a command result. data ResultError -- | An error reported by the DB. ServerError :: ByteString -> ByteString -> Maybe ByteString -> Maybe ByteString -> Maybe Int -> ResultError -- | The database returned an unexpected result. Indicates an improper -- statement or a schema mismatch. UnexpectedResult :: Text -> ResultError -- | An error of the row reader, preceded by the indexes of the row and -- column. RowError :: Int -> Int -> RowError -> ResultError -- | An unexpected amount of rows. UnexpectedAmountOfRows :: Int -> ResultError -- | An error during the decoding of a specific row. data RowError -- | Appears on the attempt to parse more columns than there are in the -- result. EndOfInput :: RowError -- | Appears on the attempt to parse a NULL as some value. UnexpectedNull :: RowError -- | Appears when a wrong value parser is used. Comes with the error -- details. ValueError :: Text -> RowError module Hasql.Pipeline -- | Composable abstraction over the execution of queries in the -- pipeline mode. -- -- It allows you to issue multiple queries to the server in much fewer -- network transactions. If the amounts of sent and received data do not -- surpass the buffer sizes in the driver and on the server it will be -- just a single roundtrip. Typically the buffer size is 8KB. -- -- This execution mode is much more efficient than running queries -- directly from Session, because in session every statement -- execution involves a dedicated network roundtrip. An obvious question -- rises then: why not execute all queries like that? -- -- In situations where the parameters depend on the result of another -- query it is impossible to execute them in parallel, because the client -- needs to receive the results of one query before sending the request -- to execute the next. This reasoning is essentially the same as the one -- for the difference between Applicative and Monad. That's -- why Pipeline does not have the Monad instance. -- -- To execute Pipeline lift it into Session via -- pipeline. -- --
-- insertOrder :: Statement OrderDetails OrderId ---- -- You can lift it into the following session -- --
-- insertOrders :: [OrderDetails] -> Session [OrderId] -- insertOrders orders = -- pipeline $ -- for orders $ \order -> -- statement order Statements.insertOrder ---- --
-- selectOrderDetails :: Statement OrderId (Maybe OrderDetails) -- selectOrderProducts :: Statement OrderId [OrderProduct] -- selectOrderFinancialTransactions :: Statement OrderId [FinancialTransaction] ---- -- You can combine them into a session using the ApplicativeDo -- extension as follows: -- --
-- selectEverythingAboutOrder :: OrderId -> Session (Maybe OrderDetails, [OrderProduct], [FinancialTransaction]) -- selectEverythingAboutOrder orderId = -- pipeline $ do -- details <- statement orderId Statements.selectOrderDetails -- products <- statement orderId Statements.selectOrderProducts -- transactions <- statement orderId Statements.selectOrderFinancialTransactions -- pure (details, products, transactions) --data Pipeline a -- | Execute a statement in pipelining mode. statement :: params -> Statement params result -> Pipeline result