2018-05-23 13:33:34 +03:00
|
|
|
module Hasql.Statement
|
2022-06-20 13:54:54 +03:00
|
|
|
( Statement (..),
|
|
|
|
refineResult,
|
2019-05-27 20:44:00 +03:00
|
|
|
|
2024-03-23 13:08:15 +03:00
|
|
|
-- * Recipes
|
2019-05-27 20:44:00 +03:00
|
|
|
|
2022-06-20 13:54:54 +03:00
|
|
|
-- ** Insert many
|
|
|
|
-- $insertMany
|
|
|
|
|
|
|
|
-- ** IN and NOT IN
|
|
|
|
-- $inAndNotIn
|
|
|
|
)
|
2015-11-08 21:09:42 +03:00
|
|
|
where
|
|
|
|
|
2015-12-05 09:09:31 +03:00
|
|
|
import qualified Hasql.Decoders as Decoders
|
2024-01-27 00:23:09 +03:00
|
|
|
import qualified Hasql.Decoders.All as Decoders
|
2015-12-05 09:09:31 +03:00
|
|
|
import qualified Hasql.Encoders as Encoders
|
2024-01-27 00:23:09 +03:00
|
|
|
import Hasql.Prelude
|
2015-11-08 21:09:42 +03:00
|
|
|
|
2022-06-20 13:54:54 +03:00
|
|
|
-- |
|
2024-03-23 13:08:15 +03:00
|
|
|
-- Specification of a strictly single-statement query, which can be parameterized and prepared, encapsulating the mapping of parameters and results.
|
2022-06-20 13:54:54 +03:00
|
|
|
--
|
|
|
|
-- Following is an example of a declaration of a prepared statement with its associated codecs.
|
|
|
|
--
|
|
|
|
-- @
|
|
|
|
-- selectSum :: 'Statement' (Int64, Int64) Int64
|
2024-03-23 13:08:15 +03:00
|
|
|
-- selectSum =
|
|
|
|
-- 'Statement' sql encoder decoder True
|
|
|
|
-- where
|
|
|
|
-- sql =
|
|
|
|
-- \"select ($1 + $2)\"
|
|
|
|
-- encoder =
|
|
|
|
-- ('fst' '>$<' Encoders.'Hasql.Encoders.param' (Encoders.'Hasql.Encoders.nonNullable' Encoders.'Hasql.Encoders.int8')) '<>'
|
|
|
|
-- ('snd' '>$<' Encoders.'Hasql.Encoders.param' (Encoders.'Hasql.Encoders.nonNullable' Encoders.'Hasql.Encoders.int8'))
|
|
|
|
-- decoder =
|
|
|
|
-- Decoders.'Hasql.Decoders.singleRow' (Decoders.'Hasql.Decoders.column' (Decoders.'Hasql.Decoders.nonNullable' Decoders.'Hasql.Decoders.int8'))
|
2022-06-20 13:54:54 +03:00
|
|
|
-- @
|
|
|
|
--
|
|
|
|
-- The statement above accepts a product of two parameters of type 'Int64'
|
|
|
|
-- and produces a single result of type 'Int64'.
|
|
|
|
data Statement a b
|
2024-03-23 13:08:15 +03:00
|
|
|
= Statement
|
|
|
|
-- | SQL template.
|
|
|
|
--
|
|
|
|
-- Must be formatted according to the Postgres standard,
|
|
|
|
-- with any non-ASCII characters of the template encoded using UTF-8.
|
|
|
|
-- The parameters must be referred to using the positional notation, as in the following:
|
|
|
|
-- @$1@, @$2@, @$3@ and etc.
|
|
|
|
-- These references must be used in accordance with the order in which
|
|
|
|
-- the value encoders are specified in the parameters encoder.
|
|
|
|
ByteString
|
|
|
|
-- | Parameters encoder.
|
|
|
|
(Encoders.Params a)
|
|
|
|
-- | Decoder of result.
|
|
|
|
(Decoders.Result b)
|
|
|
|
-- | Flag, determining whether it should be prepared.
|
|
|
|
--
|
|
|
|
-- Set it to 'True' if your application has a limited amount of queries and doesn't generate the SQL dynamically.
|
|
|
|
-- This will boost the performance by allowing Postgres to avoid reconstructing the execution plan each time the query gets executed.
|
|
|
|
--
|
|
|
|
-- Note that if you're using proxying applications like @pgbouncer@, such tools may be incompatible with prepared statements.
|
|
|
|
-- So do consult their docs or just set it to 'False' to stay on the safe side.
|
|
|
|
-- It should be noted that starting from version @1.21.0@ @pgbouncer@ now does provide support for prepared statements.
|
|
|
|
Bool
|
2018-05-23 12:15:34 +03:00
|
|
|
|
2018-05-23 13:33:34 +03:00
|
|
|
instance Functor (Statement a) where
|
2018-05-23 12:15:34 +03:00
|
|
|
{-# INLINE fmap #-}
|
|
|
|
fmap = rmap
|
2015-11-08 21:09:42 +03:00
|
|
|
|
2018-05-23 13:33:34 +03:00
|
|
|
instance Profunctor Statement where
|
2018-05-23 12:15:34 +03:00
|
|
|
{-# INLINE dimap #-}
|
2018-05-23 13:33:34 +03:00
|
|
|
dimap f1 f2 (Statement template encoder decoder preparable) =
|
|
|
|
Statement template (contramap f1 encoder) (fmap f2 decoder) preparable
|
2019-05-20 19:24:27 +03:00
|
|
|
|
2022-06-20 13:54:54 +03:00
|
|
|
-- |
|
2024-03-23 13:08:15 +03:00
|
|
|
-- Refine the result of a statement,
|
|
|
|
-- causing the running session to fail with the `UnexpectedResult` error in case of a refinement failure.
|
2022-06-20 13:54:54 +03:00
|
|
|
--
|
|
|
|
-- This function is especially useful for refining the results of statements produced with
|
|
|
|
-- <http://hackage.haskell.org/package/hasql-th the \"hasql-th\" library>.
|
2020-03-21 20:54:13 +03:00
|
|
|
refineResult :: (a -> Either Text b) -> Statement params a -> Statement params b
|
|
|
|
refineResult refiner (Statement template encoder decoder preparable) =
|
|
|
|
Statement template encoder (Decoders.refineResult refiner decoder) preparable
|
|
|
|
|
2022-06-20 13:54:54 +03:00
|
|
|
-- $insertMany
|
|
|
|
--
|
2024-03-23 13:08:15 +03:00
|
|
|
-- Starting from PostgreSQL 9.4 there is an @unnest@ function which we can use in an analogous way
|
|
|
|
-- to haskell's `zip` to pass in multiple arrays of values
|
|
|
|
-- to be zipped into the rows to insert as in the following example:
|
2022-06-20 13:54:54 +03:00
|
|
|
--
|
|
|
|
-- @
|
|
|
|
-- insertMultipleLocations :: 'Statement' (Vector (UUID, Double, Double)) ()
|
2024-03-23 13:08:15 +03:00
|
|
|
-- insertMultipleLocations =
|
|
|
|
-- 'Statement' sql encoder decoder True
|
|
|
|
-- where
|
|
|
|
-- sql =
|
|
|
|
-- "insert into location (id, x, y) select * from unnest ($1, $2, $3)"
|
|
|
|
-- encoder =
|
|
|
|
-- Data.Vector.'Data.Vector.unzip3' '>$<'
|
2024-03-23 13:44:22 +03:00
|
|
|
-- Contravariant.Extras.contrazip3
|
2024-03-23 13:08:15 +03:00
|
|
|
-- (Encoders.'Encoders.param' $ Encoders.'Encoders.nonNullable' $ Encoders.'Encoders.foldableArray' $ Encoders.'Encoders.nonNullable' Encoders.'Encoders.uuid')
|
|
|
|
-- (Encoders.'Encoders.param' $ Encoders.'Encoders.nonNullable' $ Encoders.'Encoders.foldableArray' $ Encoders.'Encoders.nonNullable' Encoders.'Encoders.float8')
|
|
|
|
-- (Encoders.'Encoders.param' $ Encoders.'Encoders.nonNullable' $ Encoders.'Encoders.foldableArray' $ Encoders.'Encoders.nonNullable' Encoders.'Encoders.float8')
|
|
|
|
-- decoder =
|
|
|
|
-- Decoders.'Decoders.noResult'
|
2022-06-20 13:54:54 +03:00
|
|
|
-- @
|
|
|
|
--
|
2024-03-23 13:08:15 +03:00
|
|
|
-- This approach is much more efficient than executing a single-row insert-statement multiple times.
|
2022-06-20 13:54:54 +03:00
|
|
|
|
|
|
|
-- $inAndNotIn
|
|
|
|
--
|
2024-03-23 13:08:15 +03:00
|
|
|
-- There is a common misconception that PostgreSQL supports array
|
|
|
|
-- as the parameter for the @IN@ operator.
|
2022-06-20 13:54:54 +03:00
|
|
|
-- However Postgres only supports a syntactical list of values with it,
|
2024-03-23 13:08:15 +03:00
|
|
|
-- i.e., you have to specify each option as an individual parameter.
|
|
|
|
-- E.g., @some_expression IN ($1, $2, $3)@.
|
2022-06-20 13:54:54 +03:00
|
|
|
--
|
2024-03-23 13:08:15 +03:00
|
|
|
-- Fortunately, Postgres does provide the expected functionality for arrays with other operators:
|
2022-06-20 13:54:54 +03:00
|
|
|
--
|
2024-03-23 13:19:58 +03:00
|
|
|
-- * Use @some_expression = ANY($1)@ instead of @some_expression IN ($1)@
|
|
|
|
-- * Use @some_expression <> ALL($1)@ instead of @some_expression NOT IN ($1)@
|
2022-06-20 13:54:54 +03:00
|
|
|
--
|
2024-03-23 13:08:15 +03:00
|
|
|
-- For details refer to
|
|
|
|
-- <https://www.postgresql.org/docs/9.6/static/functions-comparisons.html#AEN20944 the PostgreSQL docs>.
|