hasql 1.6.4 → 1.6.4.1
raw patch · 47 files changed
+2370/−2519 lines, 47 filesdep +criteriondep −gaugePVP ok
version bump matches the API change (PVP)
Dependencies added: criterion
Dependencies removed: gauge
API changes (from Hackage documentation)
Files
- benchmarks/Main.hs +3/−4
- hasql.cabal +54/−202
- library/Hasql/Commands.hs +27/−0
- library/Hasql/Connection.hs +2/−2
- library/Hasql/Connection/Core.hs +50/−0
- library/Hasql/Decoders.hs +1/−1
- library/Hasql/Decoders/All.hs +407/−0
- library/Hasql/Decoders/Array.hs +28/−0
- library/Hasql/Decoders/Composite.hs +23/−0
- library/Hasql/Decoders/Result.hs +228/−0
- library/Hasql/Decoders/Results.hs +94/−0
- library/Hasql/Decoders/Row.hs +68/−0
- library/Hasql/Decoders/Value.hs +24/−0
- library/Hasql/Encoders.hs +1/−1
- library/Hasql/Encoders/All.hs +423/−0
- library/Hasql/Encoders/Array.hs +44/−0
- library/Hasql/Encoders/Params.hs +31/−0
- library/Hasql/Encoders/Value.hs +24/−0
- library/Hasql/Errors.hs +147/−0
- library/Hasql/IO.hs +162/−0
- library/Hasql/PTI.hs +230/−0
- library/Hasql/Prelude.hs +137/−0
- library/Hasql/PreparedStatementRegistry.hs +53/−0
- library/Hasql/Private/Commands.hs +0/−27
- library/Hasql/Private/Connection.hs +0/−50
- library/Hasql/Private/Decoders.hs +0/−407
- library/Hasql/Private/Decoders/Array.hs +0/−28
- library/Hasql/Private/Decoders/Composite.hs +0/−23
- library/Hasql/Private/Decoders/Result.hs +0/−228
- library/Hasql/Private/Decoders/Results.hs +0/−94
- library/Hasql/Private/Decoders/Row.hs +0/−68
- library/Hasql/Private/Decoders/Value.hs +0/−24
- library/Hasql/Private/Encoders.hs +0/−423
- library/Hasql/Private/Encoders/Array.hs +0/−44
- library/Hasql/Private/Encoders/Params.hs +0/−31
- library/Hasql/Private/Encoders/Value.hs +0/−24
- library/Hasql/Private/Errors.hs +0/−147
- library/Hasql/Private/IO.hs +0/−162
- library/Hasql/Private/PTI.hs +0/−230
- library/Hasql/Private/Prelude.hs +0/−137
- library/Hasql/Private/PreparedStatementRegistry.hs +0/−53
- library/Hasql/Private/Session.hs +0/−65
- library/Hasql/Private/Settings.hs +0/−39
- library/Hasql/Session.hs +3/−3
- library/Hasql/Session/Core.hs +65/−0
- library/Hasql/Settings.hs +39/−0
- library/Hasql/Statement.hs +2/−2
benchmarks/Main.hs view
@@ -1,15 +1,14 @@ module Main where -import qualified Data.Vector as F-import Gauge-import Gauge.Main+import Criterion+import Criterion.Main import qualified Hasql.Connection as A import qualified Hasql.Decoders as D-import qualified Hasql.Encoders as E import qualified Hasql.Session as B import qualified Hasql.Statement as C import Prelude +main :: IO () main = do Right connection <- acquireConnection
hasql.cabal view
@@ -1,6 +1,6 @@ cabal-version: 3.0 name: hasql-version: 1.6.4+version: 1.6.4.1 category: Hasql, Database, PostgreSQL synopsis: An efficient PostgreSQL driver with a flexible mapping API description:@@ -24,8 +24,7 @@ type: git location: git://github.com/nikita-volkov/hasql.git -library- hs-source-dirs: library+common base default-extensions: NoImplicitPrelude NoMonomorphismRestriction@@ -66,6 +65,19 @@ UnboxedTuples default-language: Haskell2010++common executable+ import: base+ ghc-options:+ -O2 -threaded -with-rtsopts=-N -rtsopts -funbox-strict-fields++common test+ import: base+ ghc-options: -threaded -with-rtsopts=-N++library+ import: base+ hs-source-dirs: library exposed-modules: Hasql.Connection Hasql.Decoders@@ -74,26 +86,26 @@ Hasql.Statement other-modules:- Hasql.Private.Commands- Hasql.Private.Connection- Hasql.Private.Decoders- Hasql.Private.Decoders.Array- Hasql.Private.Decoders.Composite- Hasql.Private.Decoders.Result- Hasql.Private.Decoders.Results- Hasql.Private.Decoders.Row- Hasql.Private.Decoders.Value- Hasql.Private.Encoders- Hasql.Private.Encoders.Array- Hasql.Private.Encoders.Params- Hasql.Private.Encoders.Value- Hasql.Private.Errors- Hasql.Private.IO- Hasql.Private.Prelude- Hasql.Private.PreparedStatementRegistry- Hasql.Private.PTI- Hasql.Private.Session- Hasql.Private.Settings+ Hasql.Commands+ Hasql.Connection.Core+ Hasql.Decoders.All+ Hasql.Decoders.Array+ Hasql.Decoders.Composite+ Hasql.Decoders.Result+ Hasql.Decoders.Results+ Hasql.Decoders.Row+ Hasql.Decoders.Value+ Hasql.Encoders.All+ Hasql.Encoders.Array+ Hasql.Encoders.Params+ Hasql.Encoders.Value+ Hasql.Errors+ Hasql.IO+ Hasql.Prelude+ Hasql.PreparedStatementRegistry+ Hasql.PTI+ Hasql.Session.Core+ Hasql.Settings build-depends: , aeson >=2 && <3@@ -119,49 +131,10 @@ , vector >=0.10 && <0.14 test-suite tasty- type: exitcode-stdio-1.0- hs-source-dirs: tasty- main-is: Main.hs- default-extensions:- NoImplicitPrelude- NoMonomorphismRestriction- Arrows- BangPatterns- ConstraintKinds- DataKinds- DefaultSignatures- DeriveDataTypeable- DeriveFoldable- DeriveFunctor- DeriveGeneric- DeriveTraversable- EmptyDataDecls- FlexibleContexts- FlexibleInstances- FunctionalDependencies- GADTs- GeneralizedNewtypeDeriving- LambdaCase- LiberalTypeSynonyms- MagicHash- MultiParamTypeClasses- MultiWayIf- OverloadedStrings- ParallelListComp- PatternGuards- QuasiQuotes- RankNTypes- RecordWildCards- RoleAnnotations- ScopedTypeVariables- StandaloneDeriving- TemplateHaskell- TupleSections- TypeFamilies- TypeOperators- UnboxedTuples-- default-language: Haskell2010+ import: base+ type: exitcode-stdio-1.0+ hs-source-dirs: tasty+ main-is: Main.hs other-modules: Main.Connection Main.DSL@@ -178,152 +151,31 @@ , tasty-quickcheck >=0.9 && <0.11 test-suite threads-test- type: exitcode-stdio-1.0- hs-source-dirs: threads-test- main-is: Main.hs- default-extensions:- NoImplicitPrelude- NoMonomorphismRestriction- Arrows- BangPatterns- ConstraintKinds- DataKinds- DefaultSignatures- DeriveDataTypeable- DeriveFoldable- DeriveFunctor- DeriveGeneric- DeriveTraversable- EmptyDataDecls- FlexibleContexts- FlexibleInstances- FunctionalDependencies- GADTs- GeneralizedNewtypeDeriving- LambdaCase- LiberalTypeSynonyms- MagicHash- MultiParamTypeClasses- MultiWayIf- OverloadedStrings- ParallelListComp- PatternGuards- QuasiQuotes- RankNTypes- RecordWildCards- RoleAnnotations- ScopedTypeVariables- StandaloneDeriving- TemplateHaskell- TupleSections- TypeFamilies- TypeOperators- UnboxedTuples-- default-language: Haskell2010- other-modules: Main.Statements- ghc-options: -O2 -threaded -with-rtsopts=-N+ import: test+ type: exitcode-stdio-1.0+ hs-source-dirs: threads-test+ main-is: Main.hs+ other-modules: Main.Statements build-depends: , hasql , rerebase benchmark benchmarks- type: exitcode-stdio-1.0- hs-source-dirs: benchmarks- main-is: Main.hs- default-extensions:- NoImplicitPrelude- NoMonomorphismRestriction- Arrows- BangPatterns- ConstraintKinds- DataKinds- DefaultSignatures- DeriveDataTypeable- DeriveFoldable- DeriveFunctor- DeriveGeneric- DeriveTraversable- EmptyDataDecls- FlexibleContexts- FlexibleInstances- FunctionalDependencies- GADTs- GeneralizedNewtypeDeriving- LambdaCase- LiberalTypeSynonyms- MagicHash- MultiParamTypeClasses- MultiWayIf- OverloadedStrings- ParallelListComp- PatternGuards- QuasiQuotes- RankNTypes- RecordWildCards- RoleAnnotations- ScopedTypeVariables- StandaloneDeriving- TemplateHaskell- TupleSections- TypeFamilies- TypeOperators- UnboxedTuples-- default-language: Haskell2010- ghc-options:- -O2 -threaded -with-rtsopts=-N -rtsopts -funbox-strict-fields-+ import: executable+ type: exitcode-stdio-1.0+ hs-source-dirs: benchmarks+ main-is: Main.hs build-depends:- , gauge >=0.2.5 && <0.3+ , criterion >=1.6 && <2 , hasql , rerebase <2 test-suite profiling- type: exitcode-stdio-1.0- hs-source-dirs: profiling- main-is: Main.hs- default-extensions:- NoImplicitPrelude- NoMonomorphismRestriction- Arrows- BangPatterns- ConstraintKinds- DataKinds- DefaultSignatures- DeriveDataTypeable- DeriveFoldable- DeriveFunctor- DeriveGeneric- DeriveTraversable- EmptyDataDecls- FlexibleContexts- FlexibleInstances- FunctionalDependencies- GADTs- GeneralizedNewtypeDeriving- LambdaCase- LiberalTypeSynonyms- MagicHash- MultiParamTypeClasses- MultiWayIf- OverloadedStrings- ParallelListComp- PatternGuards- QuasiQuotes- RankNTypes- RecordWildCards- RoleAnnotations- ScopedTypeVariables- StandaloneDeriving- TemplateHaskell- TupleSections- TypeFamilies- TypeOperators- UnboxedTuples-- default-language: Haskell2010- ghc-options: -O2 -threaded -rtsopts+ import: base+ type: exitcode-stdio-1.0+ hs-source-dirs: profiling+ main-is: Main.hs+ ghc-options: -O2 -threaded -rtsopts build-depends: , hasql , rerebase >=1 && <2
+ library/Hasql/Commands.hs view
@@ -0,0 +1,27 @@+module Hasql.Commands+ ( Commands,+ asBytes,+ setEncodersToUTF8,+ setMinClientMessagesToWarning,+ )+where++import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Lazy as BL+import Hasql.Prelude++newtype Commands+ = Commands (DList BB.Builder)+ deriving (Semigroup, Monoid)++asBytes :: Commands -> ByteString+asBytes (Commands list) =+ BL.toStrict $ BB.toLazyByteString $ foldMap (<> BB.char7 ';') $ list++setEncodersToUTF8 :: Commands+setEncodersToUTF8 =+ Commands (pure "SET client_encoding = 'UTF8'")++setMinClientMessagesToWarning :: Commands+setMinClientMessagesToWarning =+ Commands (pure "SET client_min_messages TO WARNING")
library/Hasql/Connection.hs view
@@ -11,5 +11,5 @@ ) where -import Hasql.Private.Connection-import Hasql.Private.Settings+import Hasql.Connection.Core+import Hasql.Settings
+ library/Hasql/Connection/Core.hs view
@@ -0,0 +1,50 @@+-- |+-- This module provides a low-level effectful API dealing with the connections to the database.+module Hasql.Connection.Core where++import qualified Database.PostgreSQL.LibPQ as LibPQ+import qualified Hasql.IO as IO+import Hasql.Prelude+import qualified Hasql.PreparedStatementRegistry as PreparedStatementRegistry+import qualified Hasql.Settings as Settings++-- |+-- A single connection to the database.+data Connection+ = Connection !(MVar LibPQ.Connection) !Bool !PreparedStatementRegistry.PreparedStatementRegistry++-- |+-- Possible details of the connection acquistion error.+type ConnectionError =+ Maybe ByteString++-- |+-- Acquire a connection using the provided settings encoded according to the PostgreSQL format.+acquire :: Settings.Settings -> IO (Either ConnectionError Connection)+acquire settings =+ {-# SCC "acquire" #-}+ runExceptT $ do+ pqConnection <- lift (IO.acquireConnection settings)+ lift (IO.checkConnectionStatus pqConnection) >>= traverse throwError+ lift (IO.initConnection pqConnection)+ integerDatetimes <- lift (IO.getIntegerDatetimes pqConnection)+ registry <- lift (IO.acquirePreparedStatementRegistry)+ pqConnectionRef <- lift (newMVar pqConnection)+ pure (Connection pqConnectionRef integerDatetimes registry)++-- |+-- Release the connection.+release :: Connection -> IO ()+release (Connection pqConnectionRef _ _) =+ mask_ $ do+ nullConnection <- LibPQ.newNullConnection+ pqConnection <- swapMVar pqConnectionRef nullConnection+ IO.releaseConnection pqConnection++-- |+-- Execute an operation on the raw @libpq@ 'LibPQ.Connection'.+--+-- The access to the connection is exclusive.+withLibPQConnection :: Connection -> (LibPQ.Connection -> IO a) -> IO a+withLibPQConnection (Connection pqConnectionRef _ _) =+ withMVar pqConnectionRef
library/Hasql/Decoders.hs view
@@ -69,4 +69,4 @@ ) where -import Hasql.Private.Decoders+import Hasql.Decoders.All
+ library/Hasql/Decoders/All.hs view
@@ -0,0 +1,407 @@+-- |+-- A DSL for declaration of result decoders.+module Hasql.Decoders.All where++import qualified Data.Aeson as Aeson+import qualified Data.Vector.Generic as GenericVector+import qualified Hasql.Decoders.Array as Array+import qualified Hasql.Decoders.Composite as Composite+import qualified Hasql.Decoders.Result as Result+import qualified Hasql.Decoders.Results as Results+import qualified Hasql.Decoders.Row as Row+import qualified Hasql.Decoders.Value as Value+import Hasql.Prelude hiding (bool, maybe)+import qualified Hasql.Prelude as Prelude+import qualified Network.IP.Addr as NetworkIp+import qualified PostgreSQL.Binary.Decoding as A++-- * Result++-- |+-- Decoder of a query result.+newtype Result a = Result (Results.Results a) deriving (Functor)++-- |+-- Decode no value from the result.+--+-- Useful for statements like @INSERT@ or @CREATE@.+{-# INLINEABLE noResult #-}+noResult :: Result ()+noResult = Result (Results.single Result.noResult)++-- |+-- Get the amount of rows affected by such statements as+-- @UPDATE@ or @DELETE@.+{-# INLINEABLE rowsAffected #-}+rowsAffected :: Result Int64+rowsAffected = Result (Results.single Result.rowsAffected)++-- |+-- Exactly one row.+-- Will raise the 'Errors.UnexpectedAmountOfRows' error if it's any other.+{-# INLINEABLE singleRow #-}+singleRow :: Row a -> Result a+singleRow (Row row) = Result (Results.single (Result.single row))++refineResult :: (a -> Either Text b) -> Result a -> Result b+refineResult refiner (Result results) = Result (Results.refine refiner results)++-- ** Multi-row traversers++-- |+-- Foldl multiple rows.+{-# INLINEABLE foldlRows #-}+foldlRows :: (a -> b -> a) -> a -> Row b -> Result a+foldlRows step init (Row row) = Result (Results.single (Result.foldl step init row))++-- |+-- Foldr multiple rows.+{-# INLINEABLE foldrRows #-}+foldrRows :: (b -> a -> a) -> a -> Row b -> Result a+foldrRows step init (Row row) = Result (Results.single (Result.foldr step init row))++-- ** Specialized multi-row results++-- |+-- Maybe one row or none.+{-# INLINEABLE rowMaybe #-}+rowMaybe :: Row a -> Result (Maybe a)+rowMaybe (Row row) = Result (Results.single (Result.maybe row))++-- |+-- Zero or more rows packed into the vector.+--+-- It's recommended to prefer this function to 'rowList',+-- since it performs notably better.+{-# INLINEABLE rowVector #-}+rowVector :: Row a -> Result (Vector a)+rowVector (Row row) = Result (Results.single (Result.vector row))++-- |+-- Zero or more rows packed into the list.+{-# INLINEABLE rowList #-}+rowList :: Row a -> Result [a]+rowList = foldrRows strictCons []++-- * Row++-- |+-- Decoder of an individual row,+-- which gets composed of column value decoders.+--+-- E.g.:+--+-- @+-- x :: 'Row' (Maybe Int64, Text, TimeOfDay)+-- x = (,,) '<$>' ('column' . 'nullable') 'int8' '<*>' ('column' . 'nonNullable') 'text' '<*>' ('column' . 'nonNullable') 'time'+-- @+newtype Row a = Row (Row.Row a)+ deriving (Functor, Applicative, Monad, MonadFail)++-- |+-- Lift an individual value decoder to a composable row decoder.+{-# INLINEABLE column #-}+column :: NullableOrNot Value a -> Row a+column = \case+ NonNullable (Value imp) -> Row (Row.nonNullValue imp)+ Nullable (Value imp) -> Row (Row.value imp)++-- * Nullability++-- |+-- Extensional specification of nullability over a generic decoder.+data NullableOrNot decoder a where+ NonNullable :: decoder a -> NullableOrNot decoder a+ Nullable :: decoder a -> NullableOrNot decoder (Maybe a)++-- |+-- Specify that a decoder produces a non-nullable value.+nonNullable :: decoder a -> NullableOrNot decoder a+nonNullable = NonNullable++-- |+-- Specify that a decoder produces a nullable value.+nullable :: decoder a -> NullableOrNot decoder (Maybe a)+nullable = Nullable++-- * Value++-- |+-- Decoder of a value.+newtype Value a = Value (Value.Value a)+ deriving (Functor)++type role Value representational++-- |+-- Decoder of the @BOOL@ values.+{-# INLINEABLE bool #-}+bool :: Value Bool+bool = Value (Value.decoder (const A.bool))++-- |+-- Decoder of the @INT2@ values.+{-# INLINEABLE int2 #-}+int2 :: Value Int16+int2 = Value (Value.decoder (const A.int))++-- |+-- Decoder of the @INT4@ values.+{-# INLINEABLE int4 #-}+int4 :: Value Int32+int4 = Value (Value.decoder (const A.int))++-- |+-- Decoder of the @INT8@ values.+{-# INLINEABLE int8 #-}+int8 :: Value Int64+int8 =+ {-# SCC "int8" #-}+ Value (Value.decoder (const ({-# SCC "int8.int" #-} A.int)))++-- |+-- Decoder of the @FLOAT4@ values.+{-# INLINEABLE float4 #-}+float4 :: Value Float+float4 = Value (Value.decoder (const A.float4))++-- |+-- Decoder of the @FLOAT8@ values.+{-# INLINEABLE float8 #-}+float8 :: Value Double+float8 = Value (Value.decoder (const A.float8))++-- |+-- Decoder of the @NUMERIC@ values.+{-# INLINEABLE numeric #-}+numeric :: Value Scientific+numeric = Value (Value.decoder (const A.numeric))++-- |+-- Decoder of the @CHAR@ values.+-- Note that it supports Unicode values.+{-# INLINEABLE char #-}+char :: Value Char+char = Value (Value.decoder (const A.char))++-- |+-- Decoder of the @TEXT@ values.+{-# INLINEABLE text #-}+text :: Value Text+text = Value (Value.decoder (const A.text_strict))++-- |+-- Decoder of the @BYTEA@ values.+{-# INLINEABLE bytea #-}+bytea :: Value ByteString+bytea = Value (Value.decoder (const A.bytea_strict))++-- |+-- Decoder of the @DATE@ values.+{-# INLINEABLE date #-}+date :: Value Day+date = Value (Value.decoder (const A.date))++-- |+-- Decoder of the @TIMESTAMP@ values.+{-# INLINEABLE timestamp #-}+timestamp :: Value LocalTime+timestamp = Value (Value.decoder (Prelude.bool A.timestamp_float A.timestamp_int))++-- |+-- 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.+{-# INLINEABLE timestamptz #-}+timestamptz :: Value UTCTime+timestamptz = Value (Value.decoder (Prelude.bool A.timestamptz_float A.timestamptz_int))++-- |+-- Decoder of the @TIME@ values.+{-# INLINEABLE time #-}+time :: Value TimeOfDay+time = Value (Value.decoder (Prelude.bool A.time_float A.time_int))++-- |+-- 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.+{-# INLINEABLE timetz #-}+timetz :: Value (TimeOfDay, TimeZone)+timetz = Value (Value.decoder (Prelude.bool A.timetz_float A.timetz_int))++-- |+-- Decoder of the @INTERVAL@ values.+{-# INLINEABLE interval #-}+interval :: Value DiffTime+interval = Value (Value.decoder (Prelude.bool A.interval_float A.interval_int))++-- |+-- Decoder of the @UUID@ values.+{-# INLINEABLE uuid #-}+uuid :: Value UUID+uuid = Value (Value.decoder (const A.uuid))++-- |+-- Decoder of the @INET@ values.+{-# INLINEABLE inet #-}+inet :: Value (NetworkIp.NetAddr NetworkIp.IP)+inet = Value (Value.decoder (const A.inet))++-- |+-- Decoder of the @JSON@ values into a JSON AST.+{-# INLINEABLE json #-}+json :: Value Aeson.Value+json = Value (Value.decoder (const A.json_ast))++-- |+-- Decoder of the @JSON@ values into a raw JSON 'ByteString'.+{-# INLINEABLE jsonBytes #-}+jsonBytes :: (ByteString -> Either Text a) -> Value a+jsonBytes fn = Value (Value.decoder (const (A.json_bytes fn)))++-- |+-- Decoder of the @JSONB@ values into a JSON AST.+{-# INLINEABLE jsonb #-}+jsonb :: Value Aeson.Value+jsonb = Value (Value.decoder (const A.jsonb_ast))++-- |+-- Decoder of the @JSONB@ values into a raw JSON 'ByteString'.+{-# INLINEABLE jsonbBytes #-}+jsonbBytes :: (ByteString -> Either Text a) -> Value a+jsonbBytes fn = Value (Value.decoder (const (A.jsonb_bytes fn)))++-- |+-- Lift a custom value decoder function to a 'Value' decoder.+{-# INLINEABLE custom #-}+custom :: (Bool -> ByteString -> Either Text a) -> Value a+custom fn = Value (Value.decoderFn fn)++-- |+-- Refine a value decoder, lifting the possible error to the session level.+{-# INLINEABLE refine #-}+refine :: (a -> Either Text b) -> Value a -> Value b+refine fn (Value v) = Value (Value.Value (\b -> A.refine fn (Value.run v b)))++-- |+-- 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'+-- @+{-# INLINEABLE hstore #-}+hstore :: (forall m. (Monad m) => Int -> m (Text, Maybe Text) -> m a) -> Value a+hstore replicateM = Value (Value.decoder (const (A.hstore replicateM A.text_strict A.text_strict)))++-- |+-- Given a partial mapping from text to value,+-- produces a decoder of that value.+enum :: (Text -> Maybe a) -> Value a+enum mapping = Value (Value.decoder (const (A.enum mapping)))++-- |+-- Lift an 'Array' decoder to a 'Value' decoder.+{-# INLINEABLE array #-}+array :: Array a -> Value a+array (Array imp) = Value (Value.decoder (Array.run imp))++-- |+-- 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'.+{-# INLINE listArray #-}+listArray :: NullableOrNot Value element -> Value [element]+listArray = array . dimension replicateM . 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.'GenericVector.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'.+{-# INLINE vectorArray #-}+vectorArray :: (GenericVector.Vector vector element) => NullableOrNot Value element -> Value (vector element)+vectorArray = array . dimension GenericVector.replicateM . element++-- |+-- Lift a 'Composite' decoder to a 'Value' decoder.+{-# INLINEABLE composite #-}+composite :: Composite a -> Value a+composite (Composite imp) = Value (Value.decoder (Composite.run imp))++-- * Array decoders++-- |+-- 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'))))+-- @+newtype Array a = Array (Array.Array a)+ deriving (Functor)++-- |+-- A function for parsing a dimension of an array.+-- Provides support for multi-dimensional arrays.+--+-- Accepts:+--+-- * An implementation of the @replicateM@ function+-- (@Control.Monad.'Control.Monad.replicateM'@, @Data.Vector.'Data.Vector.replicateM'@),+-- which determines the output value.+--+-- * A decoder of its components, which can be either another 'dimension' or 'element'.+{-# INLINEABLE dimension #-}+dimension :: (forall m. (Monad m) => Int -> m a -> m b) -> Array a -> Array b+dimension replicateM (Array imp) = Array (Array.dimension replicateM imp)++-- |+-- Lift a 'Value' decoder into an 'Array' decoder for parsing of leaf values.+{-# INLINEABLE element #-}+element :: NullableOrNot Value a -> Array a+element = \case+ NonNullable (Value imp) -> Array (Array.nonNullValue (Value.run imp))+ Nullable (Value imp) -> Array (Array.value (Value.run imp))++-- * Composite decoders++-- |+-- Composable decoder of composite values (rows, records).+newtype Composite a = Composite (Composite.Composite a)+ deriving (Functor, Applicative, Monad, MonadFail)++-- |+-- Lift a 'Value' decoder into a 'Composite' decoder for parsing of component values.+field :: NullableOrNot Value a -> Composite a+field = \case+ NonNullable (Value imp) -> Composite (Composite.nonNullValue (Value.run imp))+ Nullable (Value imp) -> Composite (Composite.value (Value.run imp))
+ library/Hasql/Decoders/Array.hs view
@@ -0,0 +1,28 @@+module Hasql.Decoders.Array where++import Hasql.Prelude+import qualified PostgreSQL.Binary.Decoding as A++newtype Array a+ = Array (ReaderT Bool A.Array a)+ deriving (Functor)++{-# INLINE run #-}+run :: Array a -> Bool -> A.Value a+run (Array imp) env =+ A.array (runReaderT imp env)++{-# INLINE dimension #-}+dimension :: (forall m. (Monad m) => Int -> m a -> m b) -> Array a -> Array b+dimension replicateM (Array imp) =+ Array $ ReaderT $ \env -> A.dimensionArray replicateM (runReaderT imp env)++{-# INLINE value #-}+value :: (Bool -> A.Value a) -> Array (Maybe a)+value decoder' =+ Array $ ReaderT $ A.nullableValueArray . decoder'++{-# INLINE nonNullValue #-}+nonNullValue :: (Bool -> A.Value a) -> Array a+nonNullValue decoder' =+ Array $ ReaderT $ A.valueArray . decoder'
+ library/Hasql/Decoders/Composite.hs view
@@ -0,0 +1,23 @@+module Hasql.Decoders.Composite where++import Hasql.Prelude+import qualified PostgreSQL.Binary.Decoding as A++newtype Composite a+ = Composite (ReaderT Bool A.Composite a)+ deriving (Functor, Applicative, Monad, MonadFail)++{-# INLINE run #-}+run :: Composite a -> Bool -> A.Value a+run (Composite imp) env =+ A.composite (runReaderT imp env)++{-# INLINE value #-}+value :: (Bool -> A.Value a) -> Composite (Maybe a)+value decoder' =+ Composite $ ReaderT $ A.nullableValueComposite . decoder'++{-# INLINE nonNullValue #-}+nonNullValue :: (Bool -> A.Value a) -> Composite a+nonNullValue decoder' =+ Composite $ ReaderT $ A.valueComposite . decoder'
+ library/Hasql/Decoders/Result.hs view
@@ -0,0 +1,228 @@+module Hasql.Decoders.Result where++import qualified Data.Attoparsec.ByteString.Char8 as Attoparsec+import qualified Data.ByteString as ByteString+import qualified Data.Vector as Vector+import qualified Data.Vector.Mutable as MutableVector+import qualified Database.PostgreSQL.LibPQ as LibPQ+import qualified Hasql.Decoders.Row as Row+import Hasql.Errors+import Hasql.Prelude hiding (many, maybe)+import qualified Hasql.Prelude as Prelude++newtype Result a+ = Result (ReaderT (Bool, LibPQ.Result) (ExceptT ResultError IO) a)+ deriving (Functor, Applicative, Monad)++{-# INLINE run #-}+run :: Result a -> (Bool, LibPQ.Result) -> IO (Either ResultError a)+run (Result reader) env =+ runExceptT (runReaderT reader env)++{-# INLINE noResult #-}+noResult :: Result ()+noResult =+ checkExecStatus $ \case+ LibPQ.CommandOk -> True+ LibPQ.TuplesOk -> True+ _ -> False++{-# INLINE rowsAffected #-}+rowsAffected :: Result Int64+rowsAffected =+ do+ checkExecStatus $ \case+ LibPQ.CommandOk -> True+ _ -> False+ Result+ $ ReaderT+ $ \(_, result) ->+ ExceptT+ $ LibPQ.cmdTuples result+ & fmap cmdTuplesReader+ where+ cmdTuplesReader =+ notNothing >=> notEmpty >=> decimal+ where+ notNothing =+ Prelude.maybe (Left (UnexpectedResult "No bytes")) Right+ notEmpty bytes =+ if ByteString.null bytes+ then Left (UnexpectedResult "Empty bytes")+ else Right bytes+ decimal bytes =+ mapLeft (\m -> UnexpectedResult ("Decimal parsing failure: " <> fromString m))+ $ Attoparsec.parseOnly (Attoparsec.decimal <* Attoparsec.endOfInput) bytes++{-# INLINE checkExecStatus #-}+checkExecStatus :: (LibPQ.ExecStatus -> Bool) -> Result ()+checkExecStatus predicate =+ {-# SCC "checkExecStatus" #-}+ do+ status <- Result $ ReaderT $ \(_, result) -> lift $ LibPQ.resultStatus result+ unless (predicate status) $ do+ case status of+ LibPQ.BadResponse -> serverError+ LibPQ.NonfatalError -> serverError+ LibPQ.FatalError -> serverError+ LibPQ.EmptyQuery -> return ()+ _ -> Result $ lift $ ExceptT $ pure $ Left $ UnexpectedResult $ "Unexpected result status: " <> (fromString $ show status)++{-# INLINE serverError #-}+serverError :: Result ()+serverError =+ Result+ $ ReaderT+ $ \(_, result) -> ExceptT $ do+ code <-+ fmap fold+ $ LibPQ.resultErrorField result LibPQ.DiagSqlstate+ message <-+ fmap fold+ $ LibPQ.resultErrorField result LibPQ.DiagMessagePrimary+ detail <-+ LibPQ.resultErrorField result LibPQ.DiagMessageDetail+ hint <-+ LibPQ.resultErrorField result LibPQ.DiagMessageHint+ position <-+ parsePosition <$> LibPQ.resultErrorField result LibPQ.DiagStatementPosition+ pure $ Left $ ServerError code message detail hint position+ where+ parsePosition = \case+ Nothing -> Nothing+ Just pos ->+ case Attoparsec.parseOnly (Attoparsec.decimal <* Attoparsec.endOfInput) pos of+ Right pos -> Just pos+ _ -> Nothing++{-# INLINE maybe #-}+maybe :: Row.Row a -> Result (Maybe a)+maybe rowDec =+ do+ checkExecStatus $ \case+ LibPQ.TuplesOk -> True+ _ -> False+ Result+ $ ReaderT+ $ \(integerDatetimes, result) -> ExceptT $ do+ maxRows <- LibPQ.ntuples result+ case maxRows of+ 0 -> return (Right Nothing)+ 1 -> do+ maxCols <- LibPQ.nfields result+ let fromRowError (col, err) = RowError 0 col err+ fmap (fmap Just . mapLeft fromRowError) $ Row.run rowDec (result, 0, maxCols, integerDatetimes)+ _ -> return (Left (UnexpectedAmountOfRows (rowToInt maxRows)))+ where+ rowToInt (LibPQ.Row n) =+ fromIntegral n++{-# INLINE single #-}+single :: Row.Row a -> Result a+single rowDec =+ do+ checkExecStatus $ \case+ LibPQ.TuplesOk -> True+ _ -> False+ Result+ $ ReaderT+ $ \(integerDatetimes, result) -> ExceptT $ do+ maxRows <- LibPQ.ntuples result+ case maxRows of+ 1 -> do+ maxCols <- LibPQ.nfields result+ let fromRowError (col, err) = RowError 0 col err+ fmap (mapLeft fromRowError) $ Row.run rowDec (result, 0, maxCols, integerDatetimes)+ _ -> return (Left (UnexpectedAmountOfRows (rowToInt maxRows)))+ where+ rowToInt (LibPQ.Row n) =+ fromIntegral n++{-# INLINE vector #-}+vector :: Row.Row a -> Result (Vector a)+vector rowDec =+ do+ checkExecStatus $ \case+ LibPQ.TuplesOk -> True+ _ -> False+ Result+ $ ReaderT+ $ \(integerDatetimes, result) -> ExceptT $ do+ maxRows <- LibPQ.ntuples result+ maxCols <- LibPQ.nfields result+ mvector <- MutableVector.unsafeNew (rowToInt maxRows)+ failureRef <- newIORef Nothing+ forMFromZero_ (rowToInt maxRows) $ \rowIndex -> do+ rowResult <- Row.run rowDec (result, intToRow rowIndex, maxCols, integerDatetimes)+ case rowResult of+ Left !(!colIndex, !x) -> writeIORef failureRef (Just (RowError rowIndex colIndex x))+ Right !x -> MutableVector.unsafeWrite mvector rowIndex x+ readIORef failureRef >>= \case+ Nothing -> Right <$> Vector.unsafeFreeze mvector+ Just x -> pure (Left x)+ where+ rowToInt (LibPQ.Row n) =+ fromIntegral n+ intToRow =+ LibPQ.Row . fromIntegral++{-# INLINE foldl #-}+foldl :: (a -> b -> a) -> a -> Row.Row b -> Result a+foldl step init rowDec =+ {-# SCC "foldl" #-}+ do+ checkExecStatus $ \case+ LibPQ.TuplesOk -> True+ _ -> False+ Result+ $ ReaderT+ $ \(integerDatetimes, result) ->+ ExceptT+ $ {-# SCC "traversal" #-}+ do+ maxRows <- LibPQ.ntuples result+ maxCols <- LibPQ.nfields result+ accRef <- newIORef init+ failureRef <- newIORef Nothing+ forMFromZero_ (rowToInt maxRows) $ \rowIndex -> do+ rowResult <- Row.run rowDec (result, intToRow rowIndex, maxCols, integerDatetimes)+ case rowResult of+ Left !(!colIndex, !x) -> writeIORef failureRef (Just (RowError rowIndex colIndex x))+ Right !x -> modifyIORef' accRef (\acc -> step acc x)+ readIORef failureRef >>= \case+ Nothing -> Right <$> readIORef accRef+ Just x -> pure (Left x)+ where+ rowToInt (LibPQ.Row n) =+ fromIntegral n+ intToRow =+ LibPQ.Row . fromIntegral++{-# INLINE foldr #-}+foldr :: (b -> a -> a) -> a -> Row.Row b -> Result a+foldr step init rowDec =+ {-# SCC "foldr" #-}+ do+ checkExecStatus $ \case+ LibPQ.TuplesOk -> True+ _ -> False+ Result+ $ ReaderT+ $ \(integerDatetimes, result) -> ExceptT $ do+ maxRows <- LibPQ.ntuples result+ maxCols <- LibPQ.nfields result+ accRef <- newIORef init+ failureRef <- newIORef Nothing+ forMToZero_ (rowToInt maxRows) $ \rowIndex -> do+ rowResult <- Row.run rowDec (result, intToRow rowIndex, maxCols, integerDatetimes)+ case rowResult of+ Left !(!colIndex, !x) -> writeIORef failureRef (Just (RowError rowIndex colIndex x))+ Right !x -> modifyIORef accRef (\acc -> step x acc)+ readIORef failureRef >>= \case+ Nothing -> Right <$> readIORef accRef+ Just x -> pure (Left x)+ where+ rowToInt (LibPQ.Row n) =+ fromIntegral n+ intToRow =+ LibPQ.Row . fromIntegral
+ library/Hasql/Decoders/Results.hs view
@@ -0,0 +1,94 @@+-- |+-- An API for retrieval of multiple results.+-- Can be used to handle:+--+-- * A single result,+--+-- * Individual results of a multi-statement query+-- with the help of "Applicative" and "Monad",+--+-- * Row-by-row fetching.+module Hasql.Decoders.Results where++import qualified Database.PostgreSQL.LibPQ as LibPQ+import qualified Hasql.Decoders.Result as Result+import Hasql.Errors+import Hasql.Prelude hiding (many, maybe)+import qualified Hasql.Prelude as Prelude++newtype Results a+ = Results (ReaderT (Bool, LibPQ.Connection) (ExceptT CommandError IO) a)+ deriving (Functor, Applicative, Monad)++{-# INLINE run #-}+run :: Results a -> (Bool, LibPQ.Connection) -> IO (Either CommandError a)+run (Results stack) env =+ runExceptT (runReaderT stack env)++{-# INLINE clientError #-}+clientError :: Results a+clientError =+ Results+ $ ReaderT+ $ \(_, connection) ->+ ExceptT+ $ fmap (Left . ClientError) (LibPQ.errorMessage connection)++-- |+-- Parse a single result.+{-# INLINE single #-}+single :: Result.Result a -> Results a+single resultDec =+ Results+ $ ReaderT+ $ \(integerDatetimes, connection) -> ExceptT $ do+ resultMaybe <- LibPQ.getResult connection+ case resultMaybe of+ Just result ->+ mapLeft ResultError <$> Result.run resultDec (integerDatetimes, result)+ Nothing ->+ fmap (Left . ClientError) (LibPQ.errorMessage connection)++-- |+-- Fetch a single result.+{-# INLINE getResult #-}+getResult :: Results LibPQ.Result+getResult =+ Results+ $ ReaderT+ $ \(_, connection) -> ExceptT $ do+ resultMaybe <- LibPQ.getResult connection+ case resultMaybe of+ Just result -> pure (Right result)+ Nothing -> fmap (Left . ClientError) (LibPQ.errorMessage connection)++-- |+-- Fetch a single result.+{-# INLINE getResultMaybe #-}+getResultMaybe :: Results (Maybe LibPQ.Result)+getResultMaybe =+ Results $ ReaderT $ \(_, connection) -> lift $ LibPQ.getResult connection++{-# INLINE dropRemainders #-}+dropRemainders :: Results ()+dropRemainders =+ {-# SCC "dropRemainders" #-}+ Results $ ReaderT $ \(integerDatetimes, connection) -> loop integerDatetimes connection+ where+ loop integerDatetimes connection =+ getResultMaybe >>= Prelude.maybe (pure ()) onResult+ where+ getResultMaybe =+ lift $ LibPQ.getResult connection+ onResult result =+ loop integerDatetimes connection <* checkErrors+ where+ checkErrors =+ ExceptT $ fmap (mapLeft ResultError) $ Result.run Result.noResult (integerDatetimes, result)++refine :: (a -> Either Text b) -> Results a -> Results b+refine refiner results = Results+ $ ReaderT+ $ \env -> ExceptT $ do+ resultEither <- run results env+ return $ resultEither >>= mapLeft (ResultError . UnexpectedResult) . refiner
+ library/Hasql/Decoders/Row.hs view
@@ -0,0 +1,68 @@+module Hasql.Decoders.Row where++import qualified Database.PostgreSQL.LibPQ as LibPQ+import qualified Hasql.Decoders.Value as Value+import Hasql.Errors+import Hasql.Prelude hiding (error)+import qualified PostgreSQL.Binary.Decoding as A++newtype Row a+ = Row (ReaderT Env (ExceptT RowError IO) a)+ deriving (Functor, Applicative, Monad)++instance MonadFail Row where+ fail = error . ValueError . fromString++data Env+ = Env !LibPQ.Result !LibPQ.Row !LibPQ.Column !Bool !(IORef LibPQ.Column)++-- * Functions++{-# INLINE run #-}+run :: Row a -> (LibPQ.Result, LibPQ.Row, LibPQ.Column, Bool) -> IO (Either (Int, RowError) a)+run (Row impl) (result, row, columnsAmount, integerDatetimes) =+ do+ columnRef <- newIORef 0+ runExceptT (runReaderT impl (Env result row columnsAmount integerDatetimes columnRef)) >>= \case+ Left e -> do+ LibPQ.Col col <- readIORef columnRef+ -- -1 because succ is applied before the error is returned+ pure $ Left (fromIntegral col - 1, e)+ Right x -> pure $ Right x++{-# INLINE error #-}+error :: RowError -> Row a+error x =+ Row (ReaderT (const (ExceptT (pure (Left x)))))++-- |+-- Next value, decoded using the provided value decoder.+{-# INLINE value #-}+value :: Value.Value a -> Row (Maybe a)+value valueDec =+ {-# SCC "value" #-}+ Row+ $ ReaderT+ $ \(Env result row columnsAmount integerDatetimes columnRef) -> ExceptT $ do+ col <- readIORef columnRef+ writeIORef columnRef (succ col)+ if col < columnsAmount+ then do+ valueMaybe <- {-# SCC "getvalue'" #-} LibPQ.getvalue' result row col+ pure+ $ case valueMaybe of+ Nothing ->+ Right Nothing+ Just value ->+ fmap Just+ $ mapLeft ValueError+ $ {-# SCC "decode" #-} A.valueParser (Value.run valueDec integerDatetimes) value+ else pure (Left EndOfInput)++-- |+-- Next value, decoded using the provided value decoder.+{-# INLINE nonNullValue #-}+nonNullValue :: Value.Value a -> Row a+nonNullValue valueDec =+ {-# SCC "nonNullValue" #-}+ value valueDec >>= maybe (error UnexpectedNull) pure
+ library/Hasql/Decoders/Value.hs view
@@ -0,0 +1,24 @@+module Hasql.Decoders.Value where++import Hasql.Prelude+import qualified PostgreSQL.Binary.Decoding as A++newtype Value a+ = Value (Bool -> A.Value a)+ deriving (Functor)++{-# INLINE run #-}+run :: Value a -> Bool -> A.Value a+run (Value imp) integerDatetimes =+ imp integerDatetimes++{-# INLINE decoder #-}+decoder :: (Bool -> A.Value a) -> Value a+decoder =+ {-# SCC "decoder" #-}+ Value++{-# INLINE decoderFn #-}+decoderFn :: (Bool -> ByteString -> Either Text a) -> Value a+decoderFn fn =+ Value $ \integerDatetimes -> A.fn $ fn integerDatetimes
library/Hasql/Encoders.hs view
@@ -60,4 +60,4 @@ ) where -import Hasql.Private.Encoders+import Hasql.Encoders.All
+ library/Hasql/Encoders/All.hs view
@@ -0,0 +1,423 @@+-- |+-- A DSL for declaration of query parameter encoders.+module Hasql.Encoders.All where++import qualified Data.Aeson as Aeson+import qualified Data.ByteString.Lazy as LazyByteString+import qualified Hasql.Encoders.Array as Array+import qualified Hasql.Encoders.Params as Params+import qualified Hasql.Encoders.Value as Value+import qualified Hasql.PTI as PTI+import Hasql.Prelude hiding (bool)+import qualified Hasql.Prelude as Prelude+import qualified Network.IP.Addr as NetworkIp+import qualified PostgreSQL.Binary.Encoding as A+import qualified Text.Builder as C++-- * Parameters Product Encoder++-- |+-- 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"+-- @+newtype Params a = Params (Params.Params a)+ deriving (Contravariant, Divisible, Decidable, Monoid, Semigroup)++-- |+-- No parameters. Same as `mempty` and `conquered`.+noParams :: Params ()+noParams = mempty++-- |+-- Lift a single parameter encoder, with its nullability specified,+-- associating it with a single placeholder.+param :: NullableOrNot Value a -> Params a+param = \case+ NonNullable (Value valueEnc) -> Params (Params.value valueEnc)+ Nullable (Value valueEnc) -> Params (Params.nullableValue valueEnc)++-- * Nullability++-- |+-- Extensional specification of nullability over a generic encoder.+data NullableOrNot encoder a where+ NonNullable :: encoder a -> NullableOrNot encoder a+ Nullable :: encoder a -> NullableOrNot encoder (Maybe a)++-- |+-- Specify that an encoder produces a non-nullable value.+nonNullable :: encoder a -> NullableOrNot encoder a+nonNullable = NonNullable++-- |+-- Specify that an encoder produces a nullable value.+nullable :: encoder a -> NullableOrNot encoder (Maybe a)+nullable = Nullable++-- * Value++-- |+-- Value encoder.+newtype Value a = Value (Value.Value a)+ deriving (Contravariant)++-- |+-- Encoder of @BOOL@ values.+{-# INLINEABLE bool #-}+bool :: Value Bool+bool = Value (Value.unsafePTIWithShow PTI.bool (const A.bool))++-- |+-- Encoder of @INT2@ values.+{-# INLINEABLE int2 #-}+int2 :: Value Int16+int2 = Value (Value.unsafePTIWithShow PTI.int2 (const A.int2_int16))++-- |+-- Encoder of @INT4@ values.+{-# INLINEABLE int4 #-}+int4 :: Value Int32+int4 = Value (Value.unsafePTIWithShow PTI.int4 (const A.int4_int32))++-- |+-- Encoder of @INT8@ values.+{-# INLINEABLE int8 #-}+int8 :: Value Int64+int8 = Value (Value.unsafePTIWithShow PTI.int8 (const A.int8_int64))++-- |+-- Encoder of @FLOAT4@ values.+{-# INLINEABLE float4 #-}+float4 :: Value Float+float4 = Value (Value.unsafePTIWithShow PTI.float4 (const A.float4))++-- |+-- Encoder of @FLOAT8@ values.+{-# INLINEABLE float8 #-}+float8 :: Value Double+float8 = Value (Value.unsafePTIWithShow PTI.float8 (const A.float8))++-- |+-- Encoder of @NUMERIC@ values.+{-# INLINEABLE numeric #-}+numeric :: Value Scientific+numeric = Value (Value.unsafePTIWithShow PTI.numeric (const A.numeric))++-- |+-- Encoder of @CHAR@ values.+--+-- Note that it supports Unicode values and+-- identifies itself under the @TEXT@ OID because of that.+{-# INLINEABLE char #-}+char :: Value Char+char = Value (Value.unsafePTIWithShow PTI.text (const A.char_utf8))++-- |+-- Encoder of @TEXT@ values.+{-# INLINEABLE text #-}+text :: Value Text+text = Value (Value.unsafePTIWithShow PTI.text (const A.text_strict))++-- |+-- Encoder of @BYTEA@ values.+{-# INLINEABLE bytea #-}+bytea :: Value ByteString+bytea = Value (Value.unsafePTIWithShow PTI.bytea (const A.bytea_strict))++-- |+-- Encoder of @DATE@ values.+{-# INLINEABLE date #-}+date :: Value Day+date = Value (Value.unsafePTIWithShow PTI.date (const A.date))++-- |+-- Encoder of @TIMESTAMP@ values.+{-# INLINEABLE timestamp #-}+timestamp :: Value LocalTime+timestamp = Value (Value.unsafePTIWithShow PTI.timestamp (Prelude.bool A.timestamp_float A.timestamp_int))++-- |+-- Encoder of @TIMESTAMPTZ@ values.+{-# INLINEABLE timestamptz #-}+timestamptz :: Value UTCTime+timestamptz = Value (Value.unsafePTIWithShow PTI.timestamptz (Prelude.bool A.timestamptz_float A.timestamptz_int))++-- |+-- Encoder of @TIME@ values.+{-# INLINEABLE time #-}+time :: Value TimeOfDay+time = Value (Value.unsafePTIWithShow PTI.time (Prelude.bool A.time_float A.time_int))++-- |+-- Encoder of @TIMETZ@ values.+{-# INLINEABLE timetz #-}+timetz :: Value (TimeOfDay, TimeZone)+timetz = Value (Value.unsafePTIWithShow PTI.timetz (Prelude.bool A.timetz_float A.timetz_int))++-- |+-- Encoder of @INTERVAL@ values.+{-# INLINEABLE interval #-}+interval :: Value DiffTime+interval = Value (Value.unsafePTIWithShow PTI.interval (Prelude.bool A.interval_float A.interval_int))++-- |+-- Encoder of @UUID@ values.+{-# INLINEABLE uuid #-}+uuid :: Value UUID+uuid = Value (Value.unsafePTIWithShow PTI.uuid (const A.uuid))++-- |+-- Encoder of @INET@ values.+{-# INLINEABLE inet #-}+inet :: Value (NetworkIp.NetAddr NetworkIp.IP)+inet = Value (Value.unsafePTIWithShow PTI.inet (const A.inet))++-- |+-- Encoder of @JSON@ values from JSON AST.+{-# INLINEABLE json #-}+json :: Value Aeson.Value+json = Value (Value.unsafePTIWithShow PTI.json (const A.json_ast))++-- |+-- Encoder of @JSON@ values from raw JSON.+{-# INLINEABLE jsonBytes #-}+jsonBytes :: Value ByteString+jsonBytes = Value (Value.unsafePTIWithShow PTI.json (const A.json_bytes))++-- |+-- Encoder of @JSON@ values from raw JSON as lazy ByteString.+{-# INLINEABLE jsonLazyBytes #-}+jsonLazyBytes :: Value LazyByteString.ByteString+jsonLazyBytes = Value (Value.unsafePTIWithShow PTI.json (const A.json_bytes_lazy))++-- |+-- Encoder of @JSONB@ values from JSON AST.+{-# INLINEABLE jsonb #-}+jsonb :: Value Aeson.Value+jsonb = Value (Value.unsafePTIWithShow PTI.jsonb (const A.jsonb_ast))++-- |+-- Encoder of @JSONB@ values from raw JSON.+{-# INLINEABLE jsonbBytes #-}+jsonbBytes :: Value ByteString+jsonbBytes = Value (Value.unsafePTIWithShow PTI.jsonb (const A.jsonb_bytes))++-- |+-- Encoder of @JSONB@ values from raw JSON as lazy ByteString.+{-# INLINEABLE jsonbLazyBytes #-}+jsonbLazyBytes :: Value LazyByteString.ByteString+jsonbLazyBytes = Value (Value.unsafePTIWithShow PTI.jsonb (const A.jsonb_bytes_lazy))++-- |+-- Encoder of @OID@ values.+{-# INLINEABLE oid #-}+oid :: Value Int32+oid = Value (Value.unsafePTIWithShow PTI.oid (const A.int4_int32))++-- |+-- Encoder of @NAME@ values.+{-# INLINEABLE name #-}+name :: Value Text+name = Value (Value.unsafePTIWithShow PTI.name (const A.text_strict))++-- |+-- Given a function,+-- which maps a value into a textual enum label used on the DB side,+-- produces an encoder of that value.+{-# INLINEABLE enum #-}+enum :: (a -> Text) -> Value a+enum mapping = Value (Value.unsafePTI PTI.text (const (A.text_strict . mapping)) (C.text . mapping))++-- |+-- 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.+{-# INLINEABLE unknownEnum #-}+unknownEnum :: (a -> Text) -> Value a+unknownEnum mapping = Value (Value.unsafePTI PTI.binaryUnknown (const (A.text_strict . mapping)) (C.text . mapping))++-- |+-- 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+-- <https://www.postgresql.org/docs/10/static/protocol-overview.html#PROTOCOL-FORMAT-CODES 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.+{-# INLINEABLE unknown #-}+unknown :: Value ByteString+unknown = Value (Value.unsafePTIWithShow PTI.textUnknown (const A.bytea_strict))++-- |+-- Lift an array encoder into a value encoder.+array :: Array a -> Value a+array (Array (Array.Array valueOID arrayOID arrayEncoder renderer)) =+ let encoder env input = A.array (PTI.oidWord32 valueOID) (arrayEncoder env input)+ in Value (Value.Value arrayOID arrayOID encoder renderer)++-- |+-- Lift a composite encoder into a value encoder.+composite :: Composite a -> Value a+composite (Composite encode print) =+ Value (Value.unsafePTI PTI.binaryUnknown encodeValue printValue)+ where+ encodeValue idt val =+ A.composite $ encode val idt+ printValue val =+ "ROW (" <> C.intercalate ", " (print val) <> ")"++-- |+-- 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'.+{-# INLINE foldableArray #-}+foldableArray :: (Foldable foldable) => NullableOrNot Value element -> Value (foldable element)+foldableArray = array . dimension foldl' . element++-- * Array++-- |+-- 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.+newtype Array a = Array (Array.Array a)+ deriving (Contravariant)++-- |+-- Lifts a 'Value' encoder into an 'Array' encoder.+element :: NullableOrNot Value a -> Array a+element = \case+ NonNullable (Value (Value.Value elementOID arrayOID encoder renderer)) ->+ Array (Array.value elementOID arrayOID encoder renderer)+ Nullable (Value (Value.Value elementOID arrayOID encoder renderer)) ->+ Array (Array.nullableValue elementOID arrayOID encoder renderer)++-- |+-- Encoder of an array dimension,+-- which thus provides support for multidimensional arrays.+--+-- Accepts:+--+-- * An implementation of the left-fold operation,+-- such as @Data.Foldable.'foldl''@,+-- which determines the input value.+--+-- * A component encoder, which can be either another 'dimension' or 'element'.+{-# INLINEABLE dimension #-}+dimension :: (forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c+dimension foldl (Array imp) = Array (Array.dimension foldl imp)++-- * Composite++-- |+-- Composite or row-types encoder.+data Composite a+ = Composite+ (a -> Bool -> A.Composite)+ (a -> [C.Builder])++instance Contravariant Composite where+ contramap f (Composite encode print) =+ Composite (encode . f) (print . f)++instance Divisible Composite where+ divide f (Composite encodeL printL) (Composite encodeR printR) =+ Composite+ (\val idt -> case f val of (lVal, rVal) -> encodeL lVal idt <> encodeR rVal idt)+ (\val -> case f val of (lVal, rVal) -> printL lVal <> printR rVal)+ conquer = mempty++instance Semigroup (Composite a) where+ Composite encodeL printL <> Composite encodeR printR =+ Composite+ (\val idt -> encodeL val idt <> encodeR val idt)+ (\val -> printL val <> printR val)++instance Monoid (Composite a) where+ mempty = Composite mempty mempty++-- | Single field of a row-type.+field :: NullableOrNot Value a -> Composite a+field = \case+ NonNullable (Value (Value.Value elementOID arrayOID encode print)) ->+ Composite+ (\val idt -> A.field (PTI.oidWord32 elementOID) (encode idt val))+ (\val -> [print val])+ Nullable (Value (Value.Value elementOID arrayOID encode print)) ->+ Composite+ ( \val idt -> case val of+ Nothing -> A.nullField (PTI.oidWord32 elementOID)+ Just val -> A.field (PTI.oidWord32 elementOID) (encode idt val)+ )+ ( \val ->+ case val of+ Nothing -> ["NULL"]+ Just val -> [print val]+ )
+ library/Hasql/Encoders/Array.hs view
@@ -0,0 +1,44 @@+module Hasql.Encoders.Array where++import qualified Hasql.PTI as B+import Hasql.Prelude+import qualified PostgreSQL.Binary.Encoding as A+import qualified Text.Builder as C++data Array a+ = Array B.OID B.OID (Bool -> a -> A.Array) (a -> C.Builder)++instance Contravariant Array where+ contramap fn (Array valueOid arrayOid encoder renderer) =+ Array valueOid arrayOid (\intDateTimes -> encoder intDateTimes . fn) (renderer . fn)++{-# INLINE value #-}+value :: B.OID -> B.OID -> (Bool -> a -> A.Encoding) -> (a -> C.Builder) -> Array a+value valueOID arrayOID encoder =+ Array valueOID arrayOID (\params -> A.encodingArray . encoder params)++{-# INLINE nullableValue #-}+nullableValue :: B.OID -> B.OID -> (Bool -> a -> A.Encoding) -> (a -> C.Builder) -> Array (Maybe a)+nullableValue valueOID arrayOID encoder renderer =+ let maybeEncoder params =+ maybe A.nullArray (A.encodingArray . encoder params)+ maybeRenderer =+ maybe (C.string "null") renderer+ in Array valueOID arrayOID maybeEncoder maybeRenderer++{-# INLINE dimension #-}+dimension :: (forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c+dimension fold (Array valueOID arrayOID elEncoder elRenderer) =+ let encoder el =+ A.dimensionArray fold (elEncoder el)+ renderer els =+ let folded =+ let step builder el =+ if C.null builder+ then C.char '[' <> elRenderer el+ else builder <> C.string ", " <> elRenderer el+ in fold step mempty els+ in if C.null folded+ then C.string "[]"+ else folded <> C.char ']'+ in Array valueOID arrayOID encoder renderer
+ library/Hasql/Encoders/Params.hs view
@@ -0,0 +1,31 @@+module Hasql.Encoders.Params where++import qualified Database.PostgreSQL.LibPQ as A+import qualified Hasql.Encoders.Value as C+import qualified Hasql.PTI as D+import Hasql.Prelude+import qualified PostgreSQL.Binary.Encoding as B+import qualified Text.Builder as E++-- |+-- Encoder of some representation of a parameters product.+newtype Params a+ = Params (Op (DList (A.Oid, A.Format, Bool -> Maybe ByteString, Text)) a)+ deriving (Contravariant, Divisible, Decidable, Semigroup, Monoid)++value :: C.Value a -> Params a+value =+ contramap Just . nullableValue++nullableValue :: C.Value a -> Params (Maybe a)+nullableValue (C.Value valueOID arrayOID encode render) =+ Params+ $ Op+ $ \input ->+ let D.OID _ pqOid format =+ valueOID+ encoder env =+ fmap (B.encodingBytes . encode env) input+ rendering =+ maybe "null" (E.run . render) input+ in pure (pqOid, format, encoder, rendering)
+ library/Hasql/Encoders/Value.hs view
@@ -0,0 +1,24 @@+module Hasql.Encoders.Value where++import qualified Hasql.PTI as PTI+import Hasql.Prelude+import qualified PostgreSQL.Binary.Encoding as B+import qualified Text.Builder as C++data Value a+ = Value PTI.OID PTI.OID (Bool -> a -> B.Encoding) (a -> C.Builder)++instance Contravariant Value where+ {-# INLINE contramap #-}+ contramap f (Value valueOID arrayOID encode render) =+ Value valueOID arrayOID (\integerDatetimes input -> encode integerDatetimes (f input)) (render . f)++{-# INLINE unsafePTI #-}+unsafePTI :: PTI.PTI -> (Bool -> a -> B.Encoding) -> (a -> C.Builder) -> Value a+unsafePTI pti =+ Value (PTI.ptiOID pti) (fromMaybe (error "No array OID") (PTI.ptiArrayOID pti))++{-# INLINE unsafePTIWithShow #-}+unsafePTIWithShow :: (Show a) => PTI.PTI -> (Bool -> a -> B.Encoding) -> Value a+unsafePTIWithShow pti encode =+ unsafePTI pti encode (C.string . show)
+ library/Hasql/Errors.hs view
@@ -0,0 +1,147 @@+-- |+-- An API for retrieval of multiple results.+-- Can be used to handle:+--+-- * A single result,+--+-- * Individual results of a multi-statement query+-- with the help of "Applicative" and "Monad",+--+-- * Row-by-row fetching.+module Hasql.Errors where++import qualified Data.ByteString.Char8 as BC+import Hasql.Prelude++-- |+-- An error during the execution of a query.+-- Comes packed with the query template and a textual representation of the provided params.+data QueryError+ = QueryError ByteString [Text] CommandError+ deriving (Show, Eq, Typeable)++instance Exception QueryError where+ displayException (QueryError query params commandError) =+ let queryContext :: Maybe (ByteString, Int)+ queryContext = case commandError of+ ClientError _ -> Nothing+ ResultError resultError -> case resultError of+ ServerError _ message _ _ (Just position) -> Just (message, position)+ _ -> Nothing++ -- find the line number and position of the error+ findLineAndPos :: ByteString -> Int -> (Int, Int)+ findLineAndPos byteString errorPos =+ let (_, line, pos) =+ BC.foldl'+ ( \(total, line, pos) c ->+ case total + 1 of+ 0 -> (total, line, pos)+ cursor+ | cursor == errorPos -> (-1, line, pos + 1)+ | c == '\n' -> (total + 1, line + 1, 0)+ | otherwise -> (total + 1, line, pos + 1)+ )+ (0, 1, 0)+ byteString+ in (line, pos)++ formatErrorContext :: ByteString -> ByteString -> Int -> ByteString+ formatErrorContext query message errorPos =+ let lines = BC.lines query+ (lineNum, linePos) = findLineAndPos query errorPos+ in BC.unlines (take lineNum lines)+ <> BC.replicate (linePos - 1) ' '+ <> "^ "+ <> message++ prettyQuery :: ByteString+ prettyQuery = case queryContext of+ Nothing -> query+ Just (message, pos) -> formatErrorContext query message pos+ in "QueryError!\n"+ <> "\n Query:\n"+ <> BC.unpack prettyQuery+ <> "\n"+ <> "\n Params: "+ <> show params+ <> "\n Error: "+ <> case commandError of+ ClientError (Just message) -> "Client error: " <> show message+ ClientError Nothing -> "Unknown client error"+ ResultError resultError -> case resultError of+ ServerError code message details hint position ->+ "Server error "+ <> BC.unpack code+ <> ": "+ <> BC.unpack message+ <> maybe "" (\d -> "\n Details: " <> BC.unpack d) details+ <> maybe "" (\h -> "\n Hint: " <> BC.unpack h) hint+ UnexpectedResult message -> "Unexpected result: " <> show message+ RowError row column rowError ->+ "Row error: " <> show row <> ":" <> show column <> " " <> show rowError+ UnexpectedAmountOfRows amount ->+ "Unexpected amount of rows: " <> show amount++-- |+-- 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)+ | -- |+ -- Some error with a command result.+ ResultError ResultError+ deriving (Show, Eq)++-- |+-- An error with a command result.+data ResultError+ = -- | An error reported by the DB.+ ServerError+ -- | __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.+ ByteString+ -- | __Message__. The primary human-readable error message(typically one+ -- line). Always present.+ ByteString+ -- | __Details__. An optional secondary error message carrying more+ -- detail about the problem. Might run to multiple lines.+ (Maybe ByteString)+ -- | __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) rather than hard facts. Might run to+ -- multiple lines.+ (Maybe ByteString)+ -- | __Position__. Error cursor position as an index into the original+ -- statement string. Positions are measured in characters not bytes.+ (Maybe Int)+ | -- |+ -- 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 indexes of the row and column.+ RowError Int Int RowError+ | -- |+ -- An unexpected amount of rows.+ UnexpectedAmountOfRows Int+ deriving (Show, Eq)++-- |+-- 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+ | -- |+ -- Appears on the attempt to parse a @NULL@ as some value.+ UnexpectedNull+ | -- |+ -- Appears when a wrong value parser is used.+ -- Comes with the error details.+ ValueError Text+ deriving (Show, Eq)
+ library/Hasql/IO.hs view
@@ -0,0 +1,162 @@+-- |+-- An API of low-level IO operations.+module Hasql.IO where++import qualified Database.PostgreSQL.LibPQ as LibPQ+import qualified Hasql.Commands as Commands+import qualified Hasql.Decoders.Result as ResultDecoders+import qualified Hasql.Decoders.Results as ResultsDecoders+import qualified Hasql.Encoders.Params as ParamsEncoders+import Hasql.Errors+import Hasql.Prelude+import qualified Hasql.PreparedStatementRegistry as PreparedStatementRegistry++{-# INLINE acquireConnection #-}+acquireConnection :: ByteString -> IO LibPQ.Connection+acquireConnection =+ LibPQ.connectdb++{-# INLINE acquirePreparedStatementRegistry #-}+acquirePreparedStatementRegistry :: IO PreparedStatementRegistry.PreparedStatementRegistry+acquirePreparedStatementRegistry =+ PreparedStatementRegistry.new++{-# INLINE releaseConnection #-}+releaseConnection :: LibPQ.Connection -> IO ()+releaseConnection connection =+ LibPQ.finish connection++{-# INLINE checkConnectionStatus #-}+checkConnectionStatus :: LibPQ.Connection -> IO (Maybe (Maybe ByteString))+checkConnectionStatus c =+ do+ s <- LibPQ.status c+ case s of+ LibPQ.ConnectionOk -> return Nothing+ _ -> fmap Just (LibPQ.errorMessage c)++{-# INLINE checkServerVersion #-}+checkServerVersion :: LibPQ.Connection -> IO (Maybe Int)+checkServerVersion c =+ fmap (mfilter (< 80200) . Just) (LibPQ.serverVersion c)++{-# INLINE getIntegerDatetimes #-}+getIntegerDatetimes :: LibPQ.Connection -> IO Bool+getIntegerDatetimes c =+ fmap decodeValue $ LibPQ.parameterStatus c "integer_datetimes"+ where+ decodeValue =+ \case+ Just "on" -> True+ _ -> False++{-# INLINE initConnection #-}+initConnection :: LibPQ.Connection -> IO ()+initConnection c =+ void $ LibPQ.exec c (Commands.asBytes (Commands.setEncodersToUTF8 <> Commands.setMinClientMessagesToWarning))++{-# INLINE getResults #-}+getResults :: LibPQ.Connection -> Bool -> ResultsDecoders.Results a -> IO (Either CommandError a)+getResults connection integerDatetimes decoder =+ {-# SCC "getResults" #-}+ (<*) <$> get <*> dropRemainders+ where+ get =+ ResultsDecoders.run decoder (integerDatetimes, connection)+ dropRemainders =+ ResultsDecoders.run ResultsDecoders.dropRemainders (integerDatetimes, connection)++{-# INLINE getPreparedStatementKey #-}+getPreparedStatementKey ::+ LibPQ.Connection ->+ PreparedStatementRegistry.PreparedStatementRegistry ->+ ByteString ->+ [LibPQ.Oid] ->+ IO (Either CommandError ByteString)+getPreparedStatementKey connection registry template oidList =+ {-# SCC "getPreparedStatementKey" #-}+ PreparedStatementRegistry.update localKey onNewRemoteKey onOldRemoteKey registry+ where+ localKey =+ PreparedStatementRegistry.LocalKey template wordOIDList+ where+ wordOIDList =+ map (\(LibPQ.Oid x) -> fromIntegral x) oidList+ onNewRemoteKey key =+ do+ sent <- LibPQ.sendPrepare connection key template (mfilter (not . null) (Just oidList))+ let resultsDecoder =+ if sent+ then ResultsDecoders.single ResultDecoders.noResult+ else ResultsDecoders.clientError+ fmap resultsMapping $ getResults connection undefined resultsDecoder+ where+ resultsMapping =+ \case+ Left x -> (False, Left x)+ Right _ -> (True, Right key)+ onOldRemoteKey key =+ pure (pure key)++{-# INLINE checkedSend #-}+checkedSend :: LibPQ.Connection -> IO Bool -> IO (Either CommandError ())+checkedSend connection send =+ send >>= \case+ False -> fmap (Left . ClientError) $ LibPQ.errorMessage connection+ True -> pure (Right ())++{-# INLINE sendPreparedParametricStatement #-}+sendPreparedParametricStatement ::+ LibPQ.Connection ->+ PreparedStatementRegistry.PreparedStatementRegistry ->+ Bool ->+ ByteString ->+ ParamsEncoders.Params a ->+ a ->+ IO (Either CommandError ())+sendPreparedParametricStatement connection registry integerDatetimes template (ParamsEncoders.Params (Op encoderOp)) input =+ let (oidList, valueAndFormatList) =+ let step (oid, format, encoder, _) ~(oidList, bytesAndFormatList) =+ (,)+ (oid : oidList)+ (fmap (\bytes -> (bytes, format)) (encoder integerDatetimes) : bytesAndFormatList)+ in foldr step ([], []) (encoderOp input)+ in runExceptT $ do+ key <- ExceptT $ getPreparedStatementKey connection registry template oidList+ ExceptT $ checkedSend connection $ LibPQ.sendQueryPrepared connection key valueAndFormatList LibPQ.Binary++{-# INLINE sendUnpreparedParametricStatement #-}+sendUnpreparedParametricStatement ::+ LibPQ.Connection ->+ Bool ->+ ByteString ->+ ParamsEncoders.Params a ->+ a ->+ IO (Either CommandError ())+sendUnpreparedParametricStatement connection integerDatetimes template (ParamsEncoders.Params (Op encoderOp)) input =+ let params =+ let step (oid, format, encoder, _) acc =+ ((,,) <$> pure oid <*> encoder integerDatetimes <*> pure format) : acc+ in foldr step [] (encoderOp input)+ in checkedSend connection $ LibPQ.sendQueryParams connection template params LibPQ.Binary++{-# INLINE sendParametricStatement #-}+sendParametricStatement ::+ LibPQ.Connection ->+ Bool ->+ PreparedStatementRegistry.PreparedStatementRegistry ->+ ByteString ->+ ParamsEncoders.Params a ->+ Bool ->+ a ->+ IO (Either CommandError ())+sendParametricStatement connection integerDatetimes registry template encoder prepared params =+ {-# SCC "sendParametricStatement" #-}+ if prepared+ then sendPreparedParametricStatement connection registry integerDatetimes template encoder params+ else sendUnpreparedParametricStatement connection integerDatetimes template encoder params++{-# INLINE sendNonparametricStatement #-}+sendNonparametricStatement :: LibPQ.Connection -> ByteString -> IO (Either CommandError ())+sendNonparametricStatement connection sql =+ checkedSend connection $ LibPQ.sendQuery connection sql
+ library/Hasql/PTI.hs view
@@ -0,0 +1,230 @@+module Hasql.PTI where++import qualified Database.PostgreSQL.LibPQ as LibPQ+import Hasql.Prelude hiding (bool)++-- | A Postgresql type info+data PTI = PTI {ptiOID :: !OID, ptiArrayOID :: !(Maybe OID)}++-- | A Word32 and a LibPQ representation of an OID+data OID = OID {oidWord32 :: !Word32, oidPQ :: !LibPQ.Oid, oidFormat :: !LibPQ.Format}++mkOID :: LibPQ.Format -> Word32 -> OID+mkOID format x =+ OID x ((LibPQ.Oid . fromIntegral) x) format++mkPTI :: LibPQ.Format -> Word32 -> Maybe Word32 -> PTI+mkPTI format oid arrayOID =+ PTI (mkOID format oid) (fmap (mkOID format) arrayOID)++-- * Constants++abstime :: PTI+abstime = mkPTI LibPQ.Binary 702 (Just 1023)++aclitem :: PTI+aclitem = mkPTI LibPQ.Binary 1033 (Just 1034)++bit :: PTI+bit = mkPTI LibPQ.Binary 1560 (Just 1561)++bool :: PTI+bool = mkPTI LibPQ.Binary 16 (Just 1000)++box :: PTI+box = mkPTI LibPQ.Binary 603 (Just 1020)++bpchar :: PTI+bpchar = mkPTI LibPQ.Binary 1042 (Just 1014)++bytea :: PTI+bytea = mkPTI LibPQ.Binary 17 (Just 1001)++char :: PTI+char = mkPTI LibPQ.Binary 18 (Just 1002)++cid :: PTI+cid = mkPTI LibPQ.Binary 29 (Just 1012)++cidr :: PTI+cidr = mkPTI LibPQ.Binary 650 (Just 651)++circle :: PTI+circle = mkPTI LibPQ.Binary 718 (Just 719)++cstring :: PTI+cstring = mkPTI LibPQ.Binary 2275 (Just 1263)++date :: PTI+date = mkPTI LibPQ.Binary 1082 (Just 1182)++daterange :: PTI+daterange = mkPTI LibPQ.Binary 3912 (Just 3913)++float4 :: PTI+float4 = mkPTI LibPQ.Binary 700 (Just 1021)++float8 :: PTI+float8 = mkPTI LibPQ.Binary 701 (Just 1022)++gtsvector :: PTI+gtsvector = mkPTI LibPQ.Binary 3642 (Just 3644)++inet :: PTI+inet = mkPTI LibPQ.Binary 869 (Just 1041)++int2 :: PTI+int2 = mkPTI LibPQ.Binary 21 (Just 1005)++int2vector :: PTI+int2vector = mkPTI LibPQ.Binary 22 (Just 1006)++int4 :: PTI+int4 = mkPTI LibPQ.Binary 23 (Just 1007)++int4range :: PTI+int4range = mkPTI LibPQ.Binary 3904 (Just 3905)++int8 :: PTI+int8 = mkPTI LibPQ.Binary 20 (Just 1016)++int8range :: PTI+int8range = mkPTI LibPQ.Binary 3926 (Just 3927)++interval :: PTI+interval = mkPTI LibPQ.Binary 1186 (Just 1187)++json :: PTI+json = mkPTI LibPQ.Binary 114 (Just 199)++jsonb :: PTI+jsonb = mkPTI LibPQ.Binary 3802 (Just 3807)++line :: PTI+line = mkPTI LibPQ.Binary 628 (Just 629)++lseg :: PTI+lseg = mkPTI LibPQ.Binary 601 (Just 1018)++macaddr :: PTI+macaddr = mkPTI LibPQ.Binary 829 (Just 1040)++money :: PTI+money = mkPTI LibPQ.Binary 790 (Just 791)++name :: PTI+name = mkPTI LibPQ.Binary 19 (Just 1003)++numeric :: PTI+numeric = mkPTI LibPQ.Binary 1700 (Just 1231)++numrange :: PTI+numrange = mkPTI LibPQ.Binary 3906 (Just 3907)++oid :: PTI+oid = mkPTI LibPQ.Binary 26 (Just 1028)++oidvector :: PTI+oidvector = mkPTI LibPQ.Binary 30 (Just 1013)++path :: PTI+path = mkPTI LibPQ.Binary 602 (Just 1019)++point :: PTI+point = mkPTI LibPQ.Binary 600 (Just 1017)++polygon :: PTI+polygon = mkPTI LibPQ.Binary 604 (Just 1027)++record :: PTI+record = mkPTI LibPQ.Binary 2249 (Just 2287)++refcursor :: PTI+refcursor = mkPTI LibPQ.Binary 1790 (Just 2201)++regclass :: PTI+regclass = mkPTI LibPQ.Binary 2205 (Just 2210)++regconfig :: PTI+regconfig = mkPTI LibPQ.Binary 3734 (Just 3735)++regdictionary :: PTI+regdictionary = mkPTI LibPQ.Binary 3769 (Just 3770)++regoper :: PTI+regoper = mkPTI LibPQ.Binary 2203 (Just 2208)++regoperator :: PTI+regoperator = mkPTI LibPQ.Binary 2204 (Just 2209)++regproc :: PTI+regproc = mkPTI LibPQ.Binary 24 (Just 1008)++regprocedure :: PTI+regprocedure = mkPTI LibPQ.Binary 2202 (Just 2207)++regtype :: PTI+regtype = mkPTI LibPQ.Binary 2206 (Just 2211)++reltime :: PTI+reltime = mkPTI LibPQ.Binary 703 (Just 1024)++text :: PTI+text = mkPTI LibPQ.Binary 25 (Just 1009)++tid :: PTI+tid = mkPTI LibPQ.Binary 27 (Just 1010)++time :: PTI+time = mkPTI LibPQ.Binary 1083 (Just 1183)++timestamp :: PTI+timestamp = mkPTI LibPQ.Binary 1114 (Just 1115)++timestamptz :: PTI+timestamptz = mkPTI LibPQ.Binary 1184 (Just 1185)++timetz :: PTI+timetz = mkPTI LibPQ.Binary 1266 (Just 1270)++tinterval :: PTI+tinterval = mkPTI LibPQ.Binary 704 (Just 1025)++tsquery :: PTI+tsquery = mkPTI LibPQ.Binary 3615 (Just 3645)++tsrange :: PTI+tsrange = mkPTI LibPQ.Binary 3908 (Just 3909)++tstzrange :: PTI+tstzrange = mkPTI LibPQ.Binary 3910 (Just 3911)++tsvector :: PTI+tsvector = mkPTI LibPQ.Binary 3614 (Just 3643)++txid_snapshot :: PTI+txid_snapshot = mkPTI LibPQ.Binary 2970 (Just 2949)++textUnknown :: PTI+textUnknown = mkPTI LibPQ.Text 705 (Just 705)++binaryUnknown :: PTI+binaryUnknown = mkPTI LibPQ.Binary 705 (Just 705)++uuid :: PTI+uuid = mkPTI LibPQ.Binary 2950 (Just 2951)++varbit :: PTI+varbit = mkPTI LibPQ.Binary 1562 (Just 1563)++varchar :: PTI+varchar = mkPTI LibPQ.Binary 1043 (Just 1015)++void :: PTI+void = mkPTI LibPQ.Binary 2278 Nothing++xid :: PTI+xid = mkPTI LibPQ.Binary 28 (Just 1011)++xml :: PTI+xml = mkPTI LibPQ.Binary 142 (Just 143)
+ library/Hasql/Prelude.hs view
@@ -0,0 +1,137 @@+module Hasql.Prelude+ ( module Exports,+ LazyByteString,+ ByteStringBuilder,+ LazyText,+ TextBuilder,+ forMToZero_,+ forMFromZero_,+ strictCons,+ mapLeft,+ )+where++import Control.Applicative as Exports hiding (WrappedArrow (..))+import Control.Arrow as Exports hiding (first, second)+import Control.Category as Exports+import Control.Concurrent as Exports+import Control.Exception as Exports+import Control.Monad as Exports hiding (fail, forM, forM_, mapM, mapM_, msum, sequence, sequence_)+import Control.Monad.Error.Class as Exports (MonadError (..))+import Control.Monad.Fail as Exports+import Control.Monad.Fix as Exports hiding (fix)+import Control.Monad.IO.Class as Exports+import Control.Monad.Reader.Class as Exports (MonadReader (..))+import Control.Monad.ST as Exports+import Control.Monad.Trans.Class as Exports+import Control.Monad.Trans.Cont as Exports hiding (callCC, shift)+import Control.Monad.Trans.Except as Exports (Except, ExceptT (ExceptT), catchE, except, mapExcept, mapExceptT, runExcept, runExceptT, throwE, withExcept, withExceptT)+import Control.Monad.Trans.Maybe as Exports+import Control.Monad.Trans.Reader as Exports (Reader, ReaderT (ReaderT), mapReader, mapReaderT, runReader, runReaderT, withReader, withReaderT)+import Control.Monad.Trans.State.Strict as Exports (State, StateT (StateT), evalState, evalStateT, execState, execStateT, mapState, mapStateT, runState, runStateT, withState, withStateT)+import Control.Monad.Trans.Writer.Strict as Exports (Writer, WriterT (..), execWriter, execWriterT, mapWriter, mapWriterT, runWriter)+import Data.Bifunctor as Exports+import Data.Bits as Exports+import Data.Bool as Exports+import Data.ByteString as Exports (ByteString)+import qualified Data.ByteString.Builder+import qualified Data.ByteString.Lazy+import Data.Char as Exports+import Data.Coerce as Exports+import Data.Complex as Exports+import Data.DList as Exports (DList)+import Data.Data as Exports+import Data.Dynamic as Exports+import Data.Either as Exports+import Data.Fixed as Exports+import Data.Foldable as Exports hiding (toList)+import Data.Function as Exports hiding (id, (.))+import Data.Functor as Exports hiding (unzip)+import Data.Functor.Compose as Exports+import Data.Functor.Contravariant as Exports+import Data.Functor.Contravariant.Divisible as Exports+import Data.Functor.Identity as Exports+import Data.Hashable as Exports (Hashable (..))+import Data.IORef as Exports+import Data.Int as Exports+import Data.Ix as Exports+import Data.List as Exports hiding (all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1, isSubsequenceOf, mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sortOn, sum, uncons)+import Data.List.NonEmpty as Exports (NonEmpty (..))+import Data.Maybe as Exports+import Data.Monoid as Exports hiding (Alt, (<>))+import Data.Ord as Exports+import Data.Profunctor.Unsafe as Exports+import Data.Proxy as Exports+import Data.Ratio as Exports+import Data.STRef as Exports+import Data.Scientific as Exports (Scientific)+import Data.Semigroup as Exports hiding (First (..), Last (..))+import Data.String as Exports+import Data.Text as Exports (Text)+import qualified Data.Text.Lazy+import qualified Data.Text.Lazy.Builder+import Data.Time as Exports+import Data.Traversable as Exports+import Data.Tuple as Exports+import Data.UUID as Exports (UUID)+import Data.Unique as Exports+import Data.Vector as Exports (Vector)+import Data.Version as Exports+import Data.Void as Exports+import Data.Word as Exports+import Debug.Trace as Exports+import Foreign.ForeignPtr as Exports+import Foreign.Ptr as Exports+import Foreign.StablePtr as Exports+import Foreign.Storable as Exports+import GHC.Conc as Exports hiding (orElse, threadWaitRead, threadWaitReadSTM, threadWaitWrite, threadWaitWriteSTM, withMVar)+import GHC.Exts as Exports (IsList (..), groupWith, inline, lazy, sortWith)+import GHC.Generics as Exports (Generic)+import GHC.IO.Exception as Exports+import GHC.OverloadedLabels as Exports+import Numeric as Exports+import System.Environment as Exports+import System.Exit as Exports+import System.IO as Exports (Handle, hClose)+import System.IO.Error as Exports+import System.IO.Unsafe as Exports+import System.Mem as Exports+import System.Mem.StableName as Exports+import System.Timeout as Exports+import Text.ParserCombinators.ReadP as Exports (ReadP, readP_to_S, readS_to_P)+import Text.ParserCombinators.ReadPrec as Exports (ReadPrec, readP_to_Prec, readPrec_to_P, readPrec_to_S, readS_to_Prec)+import Text.Printf as Exports (hPrintf, printf)+import Unsafe.Coerce as Exports+import Prelude as Exports hiding (Read, all, and, any, concat, concatMap, elem, fail, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, (.))++type LazyByteString =+ Data.ByteString.Lazy.ByteString++type ByteStringBuilder =+ Data.ByteString.Builder.Builder++type LazyText =+ Data.Text.Lazy.Text++type TextBuilder =+ Data.Text.Lazy.Builder.Builder++{-# INLINE forMToZero_ #-}+forMToZero_ :: (Applicative m) => Int -> (Int -> m a) -> m ()+forMToZero_ !startN f =+ ($ pred startN) $ fix $ \loop !n -> if n >= 0 then f n *> loop (pred n) else pure ()++{-# INLINE forMFromZero_ #-}+forMFromZero_ :: (Applicative m) => Int -> (Int -> m a) -> m ()+forMFromZero_ !endN f =+ ($ 0) $ fix $ \loop !n -> if n < endN then f n *> loop (succ n) else pure ()++{-# INLINE strictCons #-}+strictCons :: a -> [a] -> [a]+strictCons !a b =+ let !c = a : b in c++{-# INLINE mapLeft #-}+mapLeft :: (a -> c) -> Either a b -> Either c b+mapLeft f =+ either (Left . f) Right
+ library/Hasql/PreparedStatementRegistry.hs view
@@ -0,0 +1,53 @@+module Hasql.PreparedStatementRegistry+ ( PreparedStatementRegistry,+ new,+ update,+ LocalKey (..),+ )+where++import qualified ByteString.StrictBuilder as B+import qualified Data.HashTable.IO as A+import Hasql.Prelude hiding (lookup)++data PreparedStatementRegistry+ = PreparedStatementRegistry !(A.BasicHashTable LocalKey ByteString) !(IORef Word)++{-# INLINEABLE new #-}+new :: IO PreparedStatementRegistry+new =+ PreparedStatementRegistry <$> A.new <*> newIORef 0++{-# INLINEABLE update #-}+update :: LocalKey -> (ByteString -> IO (Bool, a)) -> (ByteString -> IO a) -> PreparedStatementRegistry -> IO a+update localKey onNewRemoteKey onOldRemoteKey (PreparedStatementRegistry table counter) =+ lookup >>= maybe new old+ where+ lookup =+ A.lookup table localKey+ new =+ readIORef counter >>= onN+ where+ onN n =+ do+ (save, result) <- onNewRemoteKey remoteKey+ when save $ do+ A.insert table localKey remoteKey+ writeIORef counter (succ n)+ return result+ where+ remoteKey =+ B.builderBytes . B.asciiIntegral $ n+ old =+ onOldRemoteKey++-- |+-- Local statement key.+data LocalKey+ = LocalKey !ByteString ![Word32]+ deriving (Show, Eq)++instance Hashable LocalKey where+ {-# INLINE hashWithSalt #-}+ hashWithSalt salt (LocalKey template types) =+ hashWithSalt salt template
− library/Hasql/Private/Commands.hs
@@ -1,27 +0,0 @@-module Hasql.Private.Commands- ( Commands,- asBytes,- setEncodersToUTF8,- setMinClientMessagesToWarning,- )-where--import qualified Data.ByteString.Builder as BB-import qualified Data.ByteString.Lazy as BL-import Hasql.Private.Prelude--newtype Commands- = Commands (DList BB.Builder)- deriving (Semigroup, Monoid)--asBytes :: Commands -> ByteString-asBytes (Commands list) =- BL.toStrict $ BB.toLazyByteString $ foldMap (<> BB.char7 ';') $ list--setEncodersToUTF8 :: Commands-setEncodersToUTF8 =- Commands (pure "SET client_encoding = 'UTF8'")--setMinClientMessagesToWarning :: Commands-setMinClientMessagesToWarning =- Commands (pure "SET client_min_messages TO WARNING")
− library/Hasql/Private/Connection.hs
@@ -1,50 +0,0 @@--- |--- This module provides a low-level effectful API dealing with the connections to the database.-module Hasql.Private.Connection where--import qualified Database.PostgreSQL.LibPQ as LibPQ-import qualified Hasql.Private.IO as IO-import Hasql.Private.Prelude-import qualified Hasql.Private.PreparedStatementRegistry as PreparedStatementRegistry-import qualified Hasql.Private.Settings as Settings---- |--- A single connection to the database.-data Connection- = Connection !(MVar LibPQ.Connection) !Bool !PreparedStatementRegistry.PreparedStatementRegistry---- |--- Possible details of the connection acquistion error.-type ConnectionError =- Maybe ByteString---- |--- Acquire a connection using the provided settings encoded according to the PostgreSQL format.-acquire :: Settings.Settings -> IO (Either ConnectionError Connection)-acquire settings =- {-# SCC "acquire" #-}- runExceptT $ do- pqConnection <- lift (IO.acquireConnection settings)- lift (IO.checkConnectionStatus pqConnection) >>= traverse throwError- lift (IO.initConnection pqConnection)- integerDatetimes <- lift (IO.getIntegerDatetimes pqConnection)- registry <- lift (IO.acquirePreparedStatementRegistry)- pqConnectionRef <- lift (newMVar pqConnection)- pure (Connection pqConnectionRef integerDatetimes registry)---- |--- Release the connection.-release :: Connection -> IO ()-release (Connection pqConnectionRef _ _) =- mask_ $ do- nullConnection <- LibPQ.newNullConnection- pqConnection <- swapMVar pqConnectionRef nullConnection- IO.releaseConnection pqConnection---- |--- Execute an operation on the raw @libpq@ 'LibPQ.Connection'.------ The access to the connection is exclusive.-withLibPQConnection :: Connection -> (LibPQ.Connection -> IO a) -> IO a-withLibPQConnection (Connection pqConnectionRef _ _) =- withMVar pqConnectionRef
− library/Hasql/Private/Decoders.hs
@@ -1,407 +0,0 @@--- |--- A DSL for declaration of result decoders.-module Hasql.Private.Decoders where--import qualified Data.Aeson as Aeson-import qualified Data.Vector.Generic as GenericVector-import qualified Hasql.Private.Decoders.Array as Array-import qualified Hasql.Private.Decoders.Composite as Composite-import qualified Hasql.Private.Decoders.Result as Result-import qualified Hasql.Private.Decoders.Results as Results-import qualified Hasql.Private.Decoders.Row as Row-import qualified Hasql.Private.Decoders.Value as Value-import Hasql.Private.Prelude hiding (bool, maybe)-import qualified Hasql.Private.Prelude as Prelude-import qualified Network.IP.Addr as NetworkIp-import qualified PostgreSQL.Binary.Decoding as A---- * Result---- |--- Decoder of a query result.-newtype Result a = Result (Results.Results a) deriving (Functor)---- |--- Decode no value from the result.------ Useful for statements like @INSERT@ or @CREATE@.-{-# INLINEABLE noResult #-}-noResult :: Result ()-noResult = Result (Results.single Result.noResult)---- |--- Get the amount of rows affected by such statements as--- @UPDATE@ or @DELETE@.-{-# INLINEABLE rowsAffected #-}-rowsAffected :: Result Int64-rowsAffected = Result (Results.single Result.rowsAffected)---- |--- Exactly one row.--- Will raise the 'Errors.UnexpectedAmountOfRows' error if it's any other.-{-# INLINEABLE singleRow #-}-singleRow :: Row a -> Result a-singleRow (Row row) = Result (Results.single (Result.single row))--refineResult :: (a -> Either Text b) -> Result a -> Result b-refineResult refiner (Result results) = Result (Results.refine refiner results)---- ** Multi-row traversers---- |--- Foldl multiple rows.-{-# INLINEABLE foldlRows #-}-foldlRows :: (a -> b -> a) -> a -> Row b -> Result a-foldlRows step init (Row row) = Result (Results.single (Result.foldl step init row))---- |--- Foldr multiple rows.-{-# INLINEABLE foldrRows #-}-foldrRows :: (b -> a -> a) -> a -> Row b -> Result a-foldrRows step init (Row row) = Result (Results.single (Result.foldr step init row))---- ** Specialized multi-row results---- |--- Maybe one row or none.-{-# INLINEABLE rowMaybe #-}-rowMaybe :: Row a -> Result (Maybe a)-rowMaybe (Row row) = Result (Results.single (Result.maybe row))---- |--- Zero or more rows packed into the vector.------ It's recommended to prefer this function to 'rowList',--- since it performs notably better.-{-# INLINEABLE rowVector #-}-rowVector :: Row a -> Result (Vector a)-rowVector (Row row) = Result (Results.single (Result.vector row))---- |--- Zero or more rows packed into the list.-{-# INLINEABLE rowList #-}-rowList :: Row a -> Result [a]-rowList = foldrRows strictCons []---- * Row---- |--- Decoder of an individual row,--- which gets composed of column value decoders.------ E.g.:------ @--- x :: 'Row' (Maybe Int64, Text, TimeOfDay)--- x = (,,) '<$>' ('column' . 'nullable') 'int8' '<*>' ('column' . 'nonNullable') 'text' '<*>' ('column' . 'nonNullable') 'time'--- @-newtype Row a = Row (Row.Row a)- deriving (Functor, Applicative, Monad, MonadFail)---- |--- Lift an individual value decoder to a composable row decoder.-{-# INLINEABLE column #-}-column :: NullableOrNot Value a -> Row a-column = \case- NonNullable (Value imp) -> Row (Row.nonNullValue imp)- Nullable (Value imp) -> Row (Row.value imp)---- * Nullability---- |--- Extensional specification of nullability over a generic decoder.-data NullableOrNot decoder a where- NonNullable :: decoder a -> NullableOrNot decoder a- Nullable :: decoder a -> NullableOrNot decoder (Maybe a)---- |--- Specify that a decoder produces a non-nullable value.-nonNullable :: decoder a -> NullableOrNot decoder a-nonNullable = NonNullable---- |--- Specify that a decoder produces a nullable value.-nullable :: decoder a -> NullableOrNot decoder (Maybe a)-nullable = Nullable---- * Value---- |--- Decoder of a value.-newtype Value a = Value (Value.Value a)- deriving (Functor)--type role Value representational---- |--- Decoder of the @BOOL@ values.-{-# INLINEABLE bool #-}-bool :: Value Bool-bool = Value (Value.decoder (const A.bool))---- |--- Decoder of the @INT2@ values.-{-# INLINEABLE int2 #-}-int2 :: Value Int16-int2 = Value (Value.decoder (const A.int))---- |--- Decoder of the @INT4@ values.-{-# INLINEABLE int4 #-}-int4 :: Value Int32-int4 = Value (Value.decoder (const A.int))---- |--- Decoder of the @INT8@ values.-{-# INLINEABLE int8 #-}-int8 :: Value Int64-int8 =- {-# SCC "int8" #-}- Value (Value.decoder (const ({-# SCC "int8.int" #-} A.int)))---- |--- Decoder of the @FLOAT4@ values.-{-# INLINEABLE float4 #-}-float4 :: Value Float-float4 = Value (Value.decoder (const A.float4))---- |--- Decoder of the @FLOAT8@ values.-{-# INLINEABLE float8 #-}-float8 :: Value Double-float8 = Value (Value.decoder (const A.float8))---- |--- Decoder of the @NUMERIC@ values.-{-# INLINEABLE numeric #-}-numeric :: Value Scientific-numeric = Value (Value.decoder (const A.numeric))---- |--- Decoder of the @CHAR@ values.--- Note that it supports Unicode values.-{-# INLINEABLE char #-}-char :: Value Char-char = Value (Value.decoder (const A.char))---- |--- Decoder of the @TEXT@ values.-{-# INLINEABLE text #-}-text :: Value Text-text = Value (Value.decoder (const A.text_strict))---- |--- Decoder of the @BYTEA@ values.-{-# INLINEABLE bytea #-}-bytea :: Value ByteString-bytea = Value (Value.decoder (const A.bytea_strict))---- |--- Decoder of the @DATE@ values.-{-# INLINEABLE date #-}-date :: Value Day-date = Value (Value.decoder (const A.date))---- |--- Decoder of the @TIMESTAMP@ values.-{-# INLINEABLE timestamp #-}-timestamp :: Value LocalTime-timestamp = Value (Value.decoder (Prelude.bool A.timestamp_float A.timestamp_int))---- |--- 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.-{-# INLINEABLE timestamptz #-}-timestamptz :: Value UTCTime-timestamptz = Value (Value.decoder (Prelude.bool A.timestamptz_float A.timestamptz_int))---- |--- Decoder of the @TIME@ values.-{-# INLINEABLE time #-}-time :: Value TimeOfDay-time = Value (Value.decoder (Prelude.bool A.time_float A.time_int))---- |--- 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.-{-# INLINEABLE timetz #-}-timetz :: Value (TimeOfDay, TimeZone)-timetz = Value (Value.decoder (Prelude.bool A.timetz_float A.timetz_int))---- |--- Decoder of the @INTERVAL@ values.-{-# INLINEABLE interval #-}-interval :: Value DiffTime-interval = Value (Value.decoder (Prelude.bool A.interval_float A.interval_int))---- |--- Decoder of the @UUID@ values.-{-# INLINEABLE uuid #-}-uuid :: Value UUID-uuid = Value (Value.decoder (const A.uuid))---- |--- Decoder of the @INET@ values.-{-# INLINEABLE inet #-}-inet :: Value (NetworkIp.NetAddr NetworkIp.IP)-inet = Value (Value.decoder (const A.inet))---- |--- Decoder of the @JSON@ values into a JSON AST.-{-# INLINEABLE json #-}-json :: Value Aeson.Value-json = Value (Value.decoder (const A.json_ast))---- |--- Decoder of the @JSON@ values into a raw JSON 'ByteString'.-{-# INLINEABLE jsonBytes #-}-jsonBytes :: (ByteString -> Either Text a) -> Value a-jsonBytes fn = Value (Value.decoder (const (A.json_bytes fn)))---- |--- Decoder of the @JSONB@ values into a JSON AST.-{-# INLINEABLE jsonb #-}-jsonb :: Value Aeson.Value-jsonb = Value (Value.decoder (const A.jsonb_ast))---- |--- Decoder of the @JSONB@ values into a raw JSON 'ByteString'.-{-# INLINEABLE jsonbBytes #-}-jsonbBytes :: (ByteString -> Either Text a) -> Value a-jsonbBytes fn = Value (Value.decoder (const (A.jsonb_bytes fn)))---- |--- Lift a custom value decoder function to a 'Value' decoder.-{-# INLINEABLE custom #-}-custom :: (Bool -> ByteString -> Either Text a) -> Value a-custom fn = Value (Value.decoderFn fn)---- |--- Refine a value decoder, lifting the possible error to the session level.-{-# INLINEABLE refine #-}-refine :: (a -> Either Text b) -> Value a -> Value b-refine fn (Value v) = Value (Value.Value (\b -> A.refine fn (Value.run v b)))---- |--- 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'--- @-{-# INLINEABLE hstore #-}-hstore :: (forall m. (Monad m) => Int -> m (Text, Maybe Text) -> m a) -> Value a-hstore replicateM = Value (Value.decoder (const (A.hstore replicateM A.text_strict A.text_strict)))---- |--- Given a partial mapping from text to value,--- produces a decoder of that value.-enum :: (Text -> Maybe a) -> Value a-enum mapping = Value (Value.decoder (const (A.enum mapping)))---- |--- Lift an 'Array' decoder to a 'Value' decoder.-{-# INLINEABLE array #-}-array :: Array a -> Value a-array (Array imp) = Value (Value.decoder (Array.run imp))---- |--- 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'.-{-# INLINE listArray #-}-listArray :: NullableOrNot Value element -> Value [element]-listArray = array . dimension replicateM . 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.'GenericVector.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'.-{-# INLINE vectorArray #-}-vectorArray :: (GenericVector.Vector vector element) => NullableOrNot Value element -> Value (vector element)-vectorArray = array . dimension GenericVector.replicateM . element---- |--- Lift a 'Composite' decoder to a 'Value' decoder.-{-# INLINEABLE composite #-}-composite :: Composite a -> Value a-composite (Composite imp) = Value (Value.decoder (Composite.run imp))---- * Array decoders---- |--- 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'))))--- @-newtype Array a = Array (Array.Array a)- deriving (Functor)---- |--- A function for parsing a dimension of an array.--- Provides support for multi-dimensional arrays.------ Accepts:------ * An implementation of the @replicateM@ function--- (@Control.Monad.'Control.Monad.replicateM'@, @Data.Vector.'Data.Vector.replicateM'@),--- which determines the output value.------ * A decoder of its components, which can be either another 'dimension' or 'element'.-{-# INLINEABLE dimension #-}-dimension :: (forall m. (Monad m) => Int -> m a -> m b) -> Array a -> Array b-dimension replicateM (Array imp) = Array (Array.dimension replicateM imp)---- |--- Lift a 'Value' decoder into an 'Array' decoder for parsing of leaf values.-{-# INLINEABLE element #-}-element :: NullableOrNot Value a -> Array a-element = \case- NonNullable (Value imp) -> Array (Array.nonNullValue (Value.run imp))- Nullable (Value imp) -> Array (Array.value (Value.run imp))---- * Composite decoders---- |--- Composable decoder of composite values (rows, records).-newtype Composite a = Composite (Composite.Composite a)- deriving (Functor, Applicative, Monad, MonadFail)---- |--- Lift a 'Value' decoder into a 'Composite' decoder for parsing of component values.-field :: NullableOrNot Value a -> Composite a-field = \case- NonNullable (Value imp) -> Composite (Composite.nonNullValue (Value.run imp))- Nullable (Value imp) -> Composite (Composite.value (Value.run imp))
− library/Hasql/Private/Decoders/Array.hs
@@ -1,28 +0,0 @@-module Hasql.Private.Decoders.Array where--import Hasql.Private.Prelude-import qualified PostgreSQL.Binary.Decoding as A--newtype Array a- = Array (ReaderT Bool A.Array a)- deriving (Functor)--{-# INLINE run #-}-run :: Array a -> Bool -> A.Value a-run (Array imp) env =- A.array (runReaderT imp env)--{-# INLINE dimension #-}-dimension :: (forall m. (Monad m) => Int -> m a -> m b) -> Array a -> Array b-dimension replicateM (Array imp) =- Array $ ReaderT $ \env -> A.dimensionArray replicateM (runReaderT imp env)--{-# INLINE value #-}-value :: (Bool -> A.Value a) -> Array (Maybe a)-value decoder' =- Array $ ReaderT $ A.nullableValueArray . decoder'--{-# INLINE nonNullValue #-}-nonNullValue :: (Bool -> A.Value a) -> Array a-nonNullValue decoder' =- Array $ ReaderT $ A.valueArray . decoder'
− library/Hasql/Private/Decoders/Composite.hs
@@ -1,23 +0,0 @@-module Hasql.Private.Decoders.Composite where--import Hasql.Private.Prelude-import qualified PostgreSQL.Binary.Decoding as A--newtype Composite a- = Composite (ReaderT Bool A.Composite a)- deriving (Functor, Applicative, Monad, MonadFail)--{-# INLINE run #-}-run :: Composite a -> Bool -> A.Value a-run (Composite imp) env =- A.composite (runReaderT imp env)--{-# INLINE value #-}-value :: (Bool -> A.Value a) -> Composite (Maybe a)-value decoder' =- Composite $ ReaderT $ A.nullableValueComposite . decoder'--{-# INLINE nonNullValue #-}-nonNullValue :: (Bool -> A.Value a) -> Composite a-nonNullValue decoder' =- Composite $ ReaderT $ A.valueComposite . decoder'
− library/Hasql/Private/Decoders/Result.hs
@@ -1,228 +0,0 @@-module Hasql.Private.Decoders.Result where--import qualified Data.Attoparsec.ByteString.Char8 as Attoparsec-import qualified Data.ByteString as ByteString-import qualified Data.Vector as Vector-import qualified Data.Vector.Mutable as MutableVector-import qualified Database.PostgreSQL.LibPQ as LibPQ-import qualified Hasql.Private.Decoders.Row as Row-import Hasql.Private.Errors-import Hasql.Private.Prelude hiding (many, maybe)-import qualified Hasql.Private.Prelude as Prelude--newtype Result a- = Result (ReaderT (Bool, LibPQ.Result) (ExceptT ResultError IO) a)- deriving (Functor, Applicative, Monad)--{-# INLINE run #-}-run :: Result a -> (Bool, LibPQ.Result) -> IO (Either ResultError a)-run (Result reader) env =- runExceptT (runReaderT reader env)--{-# INLINE noResult #-}-noResult :: Result ()-noResult =- checkExecStatus $ \case- LibPQ.CommandOk -> True- LibPQ.TuplesOk -> True- _ -> False--{-# INLINE rowsAffected #-}-rowsAffected :: Result Int64-rowsAffected =- do- checkExecStatus $ \case- LibPQ.CommandOk -> True- _ -> False- Result- $ ReaderT- $ \(_, result) ->- ExceptT- $ LibPQ.cmdTuples result- & fmap cmdTuplesReader- where- cmdTuplesReader =- notNothing >=> notEmpty >=> decimal- where- notNothing =- Prelude.maybe (Left (UnexpectedResult "No bytes")) Right- notEmpty bytes =- if ByteString.null bytes- then Left (UnexpectedResult "Empty bytes")- else Right bytes- decimal bytes =- mapLeft (\m -> UnexpectedResult ("Decimal parsing failure: " <> fromString m))- $ Attoparsec.parseOnly (Attoparsec.decimal <* Attoparsec.endOfInput) bytes--{-# INLINE checkExecStatus #-}-checkExecStatus :: (LibPQ.ExecStatus -> Bool) -> Result ()-checkExecStatus predicate =- {-# SCC "checkExecStatus" #-}- do- status <- Result $ ReaderT $ \(_, result) -> lift $ LibPQ.resultStatus result- unless (predicate status) $ do- case status of- LibPQ.BadResponse -> serverError- LibPQ.NonfatalError -> serverError- LibPQ.FatalError -> serverError- LibPQ.EmptyQuery -> return ()- _ -> Result $ lift $ ExceptT $ pure $ Left $ UnexpectedResult $ "Unexpected result status: " <> (fromString $ show status)--{-# INLINE serverError #-}-serverError :: Result ()-serverError =- Result- $ ReaderT- $ \(_, result) -> ExceptT $ do- code <-- fmap fold- $ LibPQ.resultErrorField result LibPQ.DiagSqlstate- message <-- fmap fold- $ LibPQ.resultErrorField result LibPQ.DiagMessagePrimary- detail <-- LibPQ.resultErrorField result LibPQ.DiagMessageDetail- hint <-- LibPQ.resultErrorField result LibPQ.DiagMessageHint- position <-- parsePosition <$> LibPQ.resultErrorField result LibPQ.DiagStatementPosition- pure $ Left $ ServerError code message detail hint position- where- parsePosition = \case- Nothing -> Nothing- Just pos ->- case Attoparsec.parseOnly (Attoparsec.decimal <* Attoparsec.endOfInput) pos of- Right pos -> Just pos- _ -> Nothing--{-# INLINE maybe #-}-maybe :: Row.Row a -> Result (Maybe a)-maybe rowDec =- do- checkExecStatus $ \case- LibPQ.TuplesOk -> True- _ -> False- Result- $ ReaderT- $ \(integerDatetimes, result) -> ExceptT $ do- maxRows <- LibPQ.ntuples result- case maxRows of- 0 -> return (Right Nothing)- 1 -> do- maxCols <- LibPQ.nfields result- let fromRowError (col, err) = RowError 0 col err- fmap (fmap Just . mapLeft fromRowError) $ Row.run rowDec (result, 0, maxCols, integerDatetimes)- _ -> return (Left (UnexpectedAmountOfRows (rowToInt maxRows)))- where- rowToInt (LibPQ.Row n) =- fromIntegral n--{-# INLINE single #-}-single :: Row.Row a -> Result a-single rowDec =- do- checkExecStatus $ \case- LibPQ.TuplesOk -> True- _ -> False- Result- $ ReaderT- $ \(integerDatetimes, result) -> ExceptT $ do- maxRows <- LibPQ.ntuples result- case maxRows of- 1 -> do- maxCols <- LibPQ.nfields result- let fromRowError (col, err) = RowError 0 col err- fmap (mapLeft fromRowError) $ Row.run rowDec (result, 0, maxCols, integerDatetimes)- _ -> return (Left (UnexpectedAmountOfRows (rowToInt maxRows)))- where- rowToInt (LibPQ.Row n) =- fromIntegral n--{-# INLINE vector #-}-vector :: Row.Row a -> Result (Vector a)-vector rowDec =- do- checkExecStatus $ \case- LibPQ.TuplesOk -> True- _ -> False- Result- $ ReaderT- $ \(integerDatetimes, result) -> ExceptT $ do- maxRows <- LibPQ.ntuples result- maxCols <- LibPQ.nfields result- mvector <- MutableVector.unsafeNew (rowToInt maxRows)- failureRef <- newIORef Nothing- forMFromZero_ (rowToInt maxRows) $ \rowIndex -> do- rowResult <- Row.run rowDec (result, intToRow rowIndex, maxCols, integerDatetimes)- case rowResult of- Left !(!colIndex, !x) -> writeIORef failureRef (Just (RowError rowIndex colIndex x))- Right !x -> MutableVector.unsafeWrite mvector rowIndex x- readIORef failureRef >>= \case- Nothing -> Right <$> Vector.unsafeFreeze mvector- Just x -> pure (Left x)- where- rowToInt (LibPQ.Row n) =- fromIntegral n- intToRow =- LibPQ.Row . fromIntegral--{-# INLINE foldl #-}-foldl :: (a -> b -> a) -> a -> Row.Row b -> Result a-foldl step init rowDec =- {-# SCC "foldl" #-}- do- checkExecStatus $ \case- LibPQ.TuplesOk -> True- _ -> False- Result- $ ReaderT- $ \(integerDatetimes, result) ->- ExceptT- $ {-# SCC "traversal" #-}- do- maxRows <- LibPQ.ntuples result- maxCols <- LibPQ.nfields result- accRef <- newIORef init- failureRef <- newIORef Nothing- forMFromZero_ (rowToInt maxRows) $ \rowIndex -> do- rowResult <- Row.run rowDec (result, intToRow rowIndex, maxCols, integerDatetimes)- case rowResult of- Left !(!colIndex, !x) -> writeIORef failureRef (Just (RowError rowIndex colIndex x))- Right !x -> modifyIORef' accRef (\acc -> step acc x)- readIORef failureRef >>= \case- Nothing -> Right <$> readIORef accRef- Just x -> pure (Left x)- where- rowToInt (LibPQ.Row n) =- fromIntegral n- intToRow =- LibPQ.Row . fromIntegral--{-# INLINE foldr #-}-foldr :: (b -> a -> a) -> a -> Row.Row b -> Result a-foldr step init rowDec =- {-# SCC "foldr" #-}- do- checkExecStatus $ \case- LibPQ.TuplesOk -> True- _ -> False- Result- $ ReaderT- $ \(integerDatetimes, result) -> ExceptT $ do- maxRows <- LibPQ.ntuples result- maxCols <- LibPQ.nfields result- accRef <- newIORef init- failureRef <- newIORef Nothing- forMToZero_ (rowToInt maxRows) $ \rowIndex -> do- rowResult <- Row.run rowDec (result, intToRow rowIndex, maxCols, integerDatetimes)- case rowResult of- Left !(!colIndex, !x) -> writeIORef failureRef (Just (RowError rowIndex colIndex x))- Right !x -> modifyIORef accRef (\acc -> step x acc)- readIORef failureRef >>= \case- Nothing -> Right <$> readIORef accRef- Just x -> pure (Left x)- where- rowToInt (LibPQ.Row n) =- fromIntegral n- intToRow =- LibPQ.Row . fromIntegral
− library/Hasql/Private/Decoders/Results.hs
@@ -1,94 +0,0 @@--- |--- An API for retrieval of multiple results.--- Can be used to handle:------ * A single result,------ * Individual results of a multi-statement query--- with the help of "Applicative" and "Monad",------ * Row-by-row fetching.-module Hasql.Private.Decoders.Results where--import qualified Database.PostgreSQL.LibPQ as LibPQ-import qualified Hasql.Private.Decoders.Result as Result-import Hasql.Private.Errors-import Hasql.Private.Prelude hiding (many, maybe)-import qualified Hasql.Private.Prelude as Prelude--newtype Results a- = Results (ReaderT (Bool, LibPQ.Connection) (ExceptT CommandError IO) a)- deriving (Functor, Applicative, Monad)--{-# INLINE run #-}-run :: Results a -> (Bool, LibPQ.Connection) -> IO (Either CommandError a)-run (Results stack) env =- runExceptT (runReaderT stack env)--{-# INLINE clientError #-}-clientError :: Results a-clientError =- Results- $ ReaderT- $ \(_, connection) ->- ExceptT- $ fmap (Left . ClientError) (LibPQ.errorMessage connection)---- |--- Parse a single result.-{-# INLINE single #-}-single :: Result.Result a -> Results a-single resultDec =- Results- $ ReaderT- $ \(integerDatetimes, connection) -> ExceptT $ do- resultMaybe <- LibPQ.getResult connection- case resultMaybe of- Just result ->- mapLeft ResultError <$> Result.run resultDec (integerDatetimes, result)- Nothing ->- fmap (Left . ClientError) (LibPQ.errorMessage connection)---- |--- Fetch a single result.-{-# INLINE getResult #-}-getResult :: Results LibPQ.Result-getResult =- Results- $ ReaderT- $ \(_, connection) -> ExceptT $ do- resultMaybe <- LibPQ.getResult connection- case resultMaybe of- Just result -> pure (Right result)- Nothing -> fmap (Left . ClientError) (LibPQ.errorMessage connection)---- |--- Fetch a single result.-{-# INLINE getResultMaybe #-}-getResultMaybe :: Results (Maybe LibPQ.Result)-getResultMaybe =- Results $ ReaderT $ \(_, connection) -> lift $ LibPQ.getResult connection--{-# INLINE dropRemainders #-}-dropRemainders :: Results ()-dropRemainders =- {-# SCC "dropRemainders" #-}- Results $ ReaderT $ \(integerDatetimes, connection) -> loop integerDatetimes connection- where- loop integerDatetimes connection =- getResultMaybe >>= Prelude.maybe (pure ()) onResult- where- getResultMaybe =- lift $ LibPQ.getResult connection- onResult result =- loop integerDatetimes connection <* checkErrors- where- checkErrors =- ExceptT $ fmap (mapLeft ResultError) $ Result.run Result.noResult (integerDatetimes, result)--refine :: (a -> Either Text b) -> Results a -> Results b-refine refiner results = Results- $ ReaderT- $ \env -> ExceptT $ do- resultEither <- run results env- return $ resultEither >>= mapLeft (ResultError . UnexpectedResult) . refiner
− library/Hasql/Private/Decoders/Row.hs
@@ -1,68 +0,0 @@-module Hasql.Private.Decoders.Row where--import qualified Database.PostgreSQL.LibPQ as LibPQ-import qualified Hasql.Private.Decoders.Value as Value-import Hasql.Private.Errors-import Hasql.Private.Prelude hiding (error)-import qualified PostgreSQL.Binary.Decoding as A--newtype Row a- = Row (ReaderT Env (ExceptT RowError IO) a)- deriving (Functor, Applicative, Monad)--instance MonadFail Row where- fail = error . ValueError . fromString--data Env- = Env !LibPQ.Result !LibPQ.Row !LibPQ.Column !Bool !(IORef LibPQ.Column)---- * Functions--{-# INLINE run #-}-run :: Row a -> (LibPQ.Result, LibPQ.Row, LibPQ.Column, Bool) -> IO (Either (Int, RowError) a)-run (Row impl) (result, row, columnsAmount, integerDatetimes) =- do- columnRef <- newIORef 0- runExceptT (runReaderT impl (Env result row columnsAmount integerDatetimes columnRef)) >>= \case- Left e -> do- LibPQ.Col col <- readIORef columnRef- -- -1 because succ is applied before the error is returned- pure $ Left (fromIntegral col - 1, e)- Right x -> pure $ Right x--{-# INLINE error #-}-error :: RowError -> Row a-error x =- Row (ReaderT (const (ExceptT (pure (Left x)))))---- |--- Next value, decoded using the provided value decoder.-{-# INLINE value #-}-value :: Value.Value a -> Row (Maybe a)-value valueDec =- {-# SCC "value" #-}- Row- $ ReaderT- $ \(Env result row columnsAmount integerDatetimes columnRef) -> ExceptT $ do- col <- readIORef columnRef- writeIORef columnRef (succ col)- if col < columnsAmount- then do- valueMaybe <- {-# SCC "getvalue'" #-} LibPQ.getvalue' result row col- pure- $ case valueMaybe of- Nothing ->- Right Nothing- Just value ->- fmap Just- $ mapLeft ValueError- $ {-# SCC "decode" #-} A.valueParser (Value.run valueDec integerDatetimes) value- else pure (Left EndOfInput)---- |--- Next value, decoded using the provided value decoder.-{-# INLINE nonNullValue #-}-nonNullValue :: Value.Value a -> Row a-nonNullValue valueDec =- {-# SCC "nonNullValue" #-}- value valueDec >>= maybe (error UnexpectedNull) pure
− library/Hasql/Private/Decoders/Value.hs
@@ -1,24 +0,0 @@-module Hasql.Private.Decoders.Value where--import Hasql.Private.Prelude-import qualified PostgreSQL.Binary.Decoding as A--newtype Value a- = Value (Bool -> A.Value a)- deriving (Functor)--{-# INLINE run #-}-run :: Value a -> Bool -> A.Value a-run (Value imp) integerDatetimes =- imp integerDatetimes--{-# INLINE decoder #-}-decoder :: (Bool -> A.Value a) -> Value a-decoder =- {-# SCC "decoder" #-}- Value--{-# INLINE decoderFn #-}-decoderFn :: (Bool -> ByteString -> Either Text a) -> Value a-decoderFn fn =- Value $ \integerDatetimes -> A.fn $ fn integerDatetimes
− library/Hasql/Private/Encoders.hs
@@ -1,423 +0,0 @@--- |--- A DSL for declaration of query parameter encoders.-module Hasql.Private.Encoders where--import qualified Data.Aeson as Aeson-import qualified Data.ByteString.Lazy as LazyByteString-import qualified Hasql.Private.Encoders.Array as Array-import qualified Hasql.Private.Encoders.Params as Params-import qualified Hasql.Private.Encoders.Value as Value-import qualified Hasql.Private.PTI as PTI-import Hasql.Private.Prelude hiding (bool)-import qualified Hasql.Private.Prelude as Prelude-import qualified Network.IP.Addr as NetworkIp-import qualified PostgreSQL.Binary.Encoding as A-import qualified Text.Builder as C---- * Parameters Product Encoder---- |--- 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"--- @-newtype Params a = Params (Params.Params a)- deriving (Contravariant, Divisible, Decidable, Monoid, Semigroup)---- |--- No parameters. Same as `mempty` and `conquered`.-noParams :: Params ()-noParams = mempty---- |--- Lift a single parameter encoder, with its nullability specified,--- associating it with a single placeholder.-param :: NullableOrNot Value a -> Params a-param = \case- NonNullable (Value valueEnc) -> Params (Params.value valueEnc)- Nullable (Value valueEnc) -> Params (Params.nullableValue valueEnc)---- * Nullability---- |--- Extensional specification of nullability over a generic encoder.-data NullableOrNot encoder a where- NonNullable :: encoder a -> NullableOrNot encoder a- Nullable :: encoder a -> NullableOrNot encoder (Maybe a)---- |--- Specify that an encoder produces a non-nullable value.-nonNullable :: encoder a -> NullableOrNot encoder a-nonNullable = NonNullable---- |--- Specify that an encoder produces a nullable value.-nullable :: encoder a -> NullableOrNot encoder (Maybe a)-nullable = Nullable---- * Value---- |--- Value encoder.-newtype Value a = Value (Value.Value a)- deriving (Contravariant)---- |--- Encoder of @BOOL@ values.-{-# INLINEABLE bool #-}-bool :: Value Bool-bool = Value (Value.unsafePTIWithShow PTI.bool (const A.bool))---- |--- Encoder of @INT2@ values.-{-# INLINEABLE int2 #-}-int2 :: Value Int16-int2 = Value (Value.unsafePTIWithShow PTI.int2 (const A.int2_int16))---- |--- Encoder of @INT4@ values.-{-# INLINEABLE int4 #-}-int4 :: Value Int32-int4 = Value (Value.unsafePTIWithShow PTI.int4 (const A.int4_int32))---- |--- Encoder of @INT8@ values.-{-# INLINEABLE int8 #-}-int8 :: Value Int64-int8 = Value (Value.unsafePTIWithShow PTI.int8 (const A.int8_int64))---- |--- Encoder of @FLOAT4@ values.-{-# INLINEABLE float4 #-}-float4 :: Value Float-float4 = Value (Value.unsafePTIWithShow PTI.float4 (const A.float4))---- |--- Encoder of @FLOAT8@ values.-{-# INLINEABLE float8 #-}-float8 :: Value Double-float8 = Value (Value.unsafePTIWithShow PTI.float8 (const A.float8))---- |--- Encoder of @NUMERIC@ values.-{-# INLINEABLE numeric #-}-numeric :: Value Scientific-numeric = Value (Value.unsafePTIWithShow PTI.numeric (const A.numeric))---- |--- Encoder of @CHAR@ values.------ Note that it supports Unicode values and--- identifies itself under the @TEXT@ OID because of that.-{-# INLINEABLE char #-}-char :: Value Char-char = Value (Value.unsafePTIWithShow PTI.text (const A.char_utf8))---- |--- Encoder of @TEXT@ values.-{-# INLINEABLE text #-}-text :: Value Text-text = Value (Value.unsafePTIWithShow PTI.text (const A.text_strict))---- |--- Encoder of @BYTEA@ values.-{-# INLINEABLE bytea #-}-bytea :: Value ByteString-bytea = Value (Value.unsafePTIWithShow PTI.bytea (const A.bytea_strict))---- |--- Encoder of @DATE@ values.-{-# INLINEABLE date #-}-date :: Value Day-date = Value (Value.unsafePTIWithShow PTI.date (const A.date))---- |--- Encoder of @TIMESTAMP@ values.-{-# INLINEABLE timestamp #-}-timestamp :: Value LocalTime-timestamp = Value (Value.unsafePTIWithShow PTI.timestamp (Prelude.bool A.timestamp_float A.timestamp_int))---- |--- Encoder of @TIMESTAMPTZ@ values.-{-# INLINEABLE timestamptz #-}-timestamptz :: Value UTCTime-timestamptz = Value (Value.unsafePTIWithShow PTI.timestamptz (Prelude.bool A.timestamptz_float A.timestamptz_int))---- |--- Encoder of @TIME@ values.-{-# INLINEABLE time #-}-time :: Value TimeOfDay-time = Value (Value.unsafePTIWithShow PTI.time (Prelude.bool A.time_float A.time_int))---- |--- Encoder of @TIMETZ@ values.-{-# INLINEABLE timetz #-}-timetz :: Value (TimeOfDay, TimeZone)-timetz = Value (Value.unsafePTIWithShow PTI.timetz (Prelude.bool A.timetz_float A.timetz_int))---- |--- Encoder of @INTERVAL@ values.-{-# INLINEABLE interval #-}-interval :: Value DiffTime-interval = Value (Value.unsafePTIWithShow PTI.interval (Prelude.bool A.interval_float A.interval_int))---- |--- Encoder of @UUID@ values.-{-# INLINEABLE uuid #-}-uuid :: Value UUID-uuid = Value (Value.unsafePTIWithShow PTI.uuid (const A.uuid))---- |--- Encoder of @INET@ values.-{-# INLINEABLE inet #-}-inet :: Value (NetworkIp.NetAddr NetworkIp.IP)-inet = Value (Value.unsafePTIWithShow PTI.inet (const A.inet))---- |--- Encoder of @JSON@ values from JSON AST.-{-# INLINEABLE json #-}-json :: Value Aeson.Value-json = Value (Value.unsafePTIWithShow PTI.json (const A.json_ast))---- |--- Encoder of @JSON@ values from raw JSON.-{-# INLINEABLE jsonBytes #-}-jsonBytes :: Value ByteString-jsonBytes = Value (Value.unsafePTIWithShow PTI.json (const A.json_bytes))---- |--- Encoder of @JSON@ values from raw JSON as lazy ByteString.-{-# INLINEABLE jsonLazyBytes #-}-jsonLazyBytes :: Value LazyByteString.ByteString-jsonLazyBytes = Value (Value.unsafePTIWithShow PTI.json (const A.json_bytes_lazy))---- |--- Encoder of @JSONB@ values from JSON AST.-{-# INLINEABLE jsonb #-}-jsonb :: Value Aeson.Value-jsonb = Value (Value.unsafePTIWithShow PTI.jsonb (const A.jsonb_ast))---- |--- Encoder of @JSONB@ values from raw JSON.-{-# INLINEABLE jsonbBytes #-}-jsonbBytes :: Value ByteString-jsonbBytes = Value (Value.unsafePTIWithShow PTI.jsonb (const A.jsonb_bytes))---- |--- Encoder of @JSONB@ values from raw JSON as lazy ByteString.-{-# INLINEABLE jsonbLazyBytes #-}-jsonbLazyBytes :: Value LazyByteString.ByteString-jsonbLazyBytes = Value (Value.unsafePTIWithShow PTI.jsonb (const A.jsonb_bytes_lazy))---- |--- Encoder of @OID@ values.-{-# INLINEABLE oid #-}-oid :: Value Int32-oid = Value (Value.unsafePTIWithShow PTI.oid (const A.int4_int32))---- |--- Encoder of @NAME@ values.-{-# INLINEABLE name #-}-name :: Value Text-name = Value (Value.unsafePTIWithShow PTI.name (const A.text_strict))---- |--- Given a function,--- which maps a value into a textual enum label used on the DB side,--- produces an encoder of that value.-{-# INLINEABLE enum #-}-enum :: (a -> Text) -> Value a-enum mapping = Value (Value.unsafePTI PTI.text (const (A.text_strict . mapping)) (C.text . mapping))---- |--- 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.-{-# INLINEABLE unknownEnum #-}-unknownEnum :: (a -> Text) -> Value a-unknownEnum mapping = Value (Value.unsafePTI PTI.binaryUnknown (const (A.text_strict . mapping)) (C.text . mapping))---- |--- 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--- <https://www.postgresql.org/docs/10/static/protocol-overview.html#PROTOCOL-FORMAT-CODES 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.-{-# INLINEABLE unknown #-}-unknown :: Value ByteString-unknown = Value (Value.unsafePTIWithShow PTI.textUnknown (const A.bytea_strict))---- |--- Lift an array encoder into a value encoder.-array :: Array a -> Value a-array (Array (Array.Array valueOID arrayOID arrayEncoder renderer)) =- let encoder env input = A.array (PTI.oidWord32 valueOID) (arrayEncoder env input)- in Value (Value.Value arrayOID arrayOID encoder renderer)---- |--- Lift a composite encoder into a value encoder.-composite :: Composite a -> Value a-composite (Composite encode print) =- Value (Value.unsafePTI PTI.binaryUnknown encodeValue printValue)- where- encodeValue idt val =- A.composite $ encode val idt- printValue val =- "ROW (" <> C.intercalate ", " (print val) <> ")"---- |--- 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'.-{-# INLINE foldableArray #-}-foldableArray :: (Foldable foldable) => NullableOrNot Value element -> Value (foldable element)-foldableArray = array . dimension foldl' . element---- * Array---- |--- 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.-newtype Array a = Array (Array.Array a)- deriving (Contravariant)---- |--- Lifts a 'Value' encoder into an 'Array' encoder.-element :: NullableOrNot Value a -> Array a-element = \case- NonNullable (Value (Value.Value elementOID arrayOID encoder renderer)) ->- Array (Array.value elementOID arrayOID encoder renderer)- Nullable (Value (Value.Value elementOID arrayOID encoder renderer)) ->- Array (Array.nullableValue elementOID arrayOID encoder renderer)---- |--- Encoder of an array dimension,--- which thus provides support for multidimensional arrays.------ Accepts:------ * An implementation of the left-fold operation,--- such as @Data.Foldable.'foldl''@,--- which determines the input value.------ * A component encoder, which can be either another 'dimension' or 'element'.-{-# INLINEABLE dimension #-}-dimension :: (forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c-dimension foldl (Array imp) = Array (Array.dimension foldl imp)---- * Composite---- |--- Composite or row-types encoder.-data Composite a- = Composite- (a -> Bool -> A.Composite)- (a -> [C.Builder])--instance Contravariant Composite where- contramap f (Composite encode print) =- Composite (encode . f) (print . f)--instance Divisible Composite where- divide f (Composite encodeL printL) (Composite encodeR printR) =- Composite- (\val idt -> case f val of (lVal, rVal) -> encodeL lVal idt <> encodeR rVal idt)- (\val -> case f val of (lVal, rVal) -> printL lVal <> printR rVal)- conquer = mempty--instance Semigroup (Composite a) where- Composite encodeL printL <> Composite encodeR printR =- Composite- (\val idt -> encodeL val idt <> encodeR val idt)- (\val -> printL val <> printR val)--instance Monoid (Composite a) where- mempty = Composite mempty mempty---- | Single field of a row-type.-field :: NullableOrNot Value a -> Composite a-field = \case- NonNullable (Value (Value.Value elementOID arrayOID encode print)) ->- Composite- (\val idt -> A.field (PTI.oidWord32 elementOID) (encode idt val))- (\val -> [print val])- Nullable (Value (Value.Value elementOID arrayOID encode print)) ->- Composite- ( \val idt -> case val of- Nothing -> A.nullField (PTI.oidWord32 elementOID)- Just val -> A.field (PTI.oidWord32 elementOID) (encode idt val)- )- ( \val ->- case val of- Nothing -> ["NULL"]- Just val -> [print val]- )
− library/Hasql/Private/Encoders/Array.hs
@@ -1,44 +0,0 @@-module Hasql.Private.Encoders.Array where--import qualified Hasql.Private.PTI as B-import Hasql.Private.Prelude-import qualified PostgreSQL.Binary.Encoding as A-import qualified Text.Builder as C--data Array a- = Array B.OID B.OID (Bool -> a -> A.Array) (a -> C.Builder)--instance Contravariant Array where- contramap fn (Array valueOid arrayOid encoder renderer) =- Array valueOid arrayOid (\intDateTimes -> encoder intDateTimes . fn) (renderer . fn)--{-# INLINE value #-}-value :: B.OID -> B.OID -> (Bool -> a -> A.Encoding) -> (a -> C.Builder) -> Array a-value valueOID arrayOID encoder =- Array valueOID arrayOID (\params -> A.encodingArray . encoder params)--{-# INLINE nullableValue #-}-nullableValue :: B.OID -> B.OID -> (Bool -> a -> A.Encoding) -> (a -> C.Builder) -> Array (Maybe a)-nullableValue valueOID arrayOID encoder renderer =- let maybeEncoder params =- maybe A.nullArray (A.encodingArray . encoder params)- maybeRenderer =- maybe (C.string "null") renderer- in Array valueOID arrayOID maybeEncoder maybeRenderer--{-# INLINE dimension #-}-dimension :: (forall a. (a -> b -> a) -> a -> c -> a) -> Array b -> Array c-dimension fold (Array valueOID arrayOID elEncoder elRenderer) =- let encoder el =- A.dimensionArray fold (elEncoder el)- renderer els =- let folded =- let step builder el =- if C.null builder- then C.char '[' <> elRenderer el- else builder <> C.string ", " <> elRenderer el- in fold step mempty els- in if C.null folded- then C.string "[]"- else folded <> C.char ']'- in Array valueOID arrayOID encoder renderer
− library/Hasql/Private/Encoders/Params.hs
@@ -1,31 +0,0 @@-module Hasql.Private.Encoders.Params where--import qualified Database.PostgreSQL.LibPQ as A-import qualified Hasql.Private.Encoders.Value as C-import qualified Hasql.Private.PTI as D-import Hasql.Private.Prelude-import qualified PostgreSQL.Binary.Encoding as B-import qualified Text.Builder as E---- |--- Encoder of some representation of a parameters product.-newtype Params a- = Params (Op (DList (A.Oid, A.Format, Bool -> Maybe ByteString, Text)) a)- deriving (Contravariant, Divisible, Decidable, Semigroup, Monoid)--value :: C.Value a -> Params a-value =- contramap Just . nullableValue--nullableValue :: C.Value a -> Params (Maybe a)-nullableValue (C.Value valueOID arrayOID encode render) =- Params- $ Op- $ \input ->- let D.OID _ pqOid format =- valueOID- encoder env =- fmap (B.encodingBytes . encode env) input- rendering =- maybe "null" (E.run . render) input- in pure (pqOid, format, encoder, rendering)
− library/Hasql/Private/Encoders/Value.hs
@@ -1,24 +0,0 @@-module Hasql.Private.Encoders.Value where--import qualified Hasql.Private.PTI as PTI-import Hasql.Private.Prelude-import qualified PostgreSQL.Binary.Encoding as B-import qualified Text.Builder as C--data Value a- = Value PTI.OID PTI.OID (Bool -> a -> B.Encoding) (a -> C.Builder)--instance Contravariant Value where- {-# INLINE contramap #-}- contramap f (Value valueOID arrayOID encode render) =- Value valueOID arrayOID (\integerDatetimes input -> encode integerDatetimes (f input)) (render . f)--{-# INLINE unsafePTI #-}-unsafePTI :: PTI.PTI -> (Bool -> a -> B.Encoding) -> (a -> C.Builder) -> Value a-unsafePTI pti =- Value (PTI.ptiOID pti) (fromMaybe (error "No array OID") (PTI.ptiArrayOID pti))--{-# INLINE unsafePTIWithShow #-}-unsafePTIWithShow :: (Show a) => PTI.PTI -> (Bool -> a -> B.Encoding) -> Value a-unsafePTIWithShow pti encode =- unsafePTI pti encode (C.string . show)
− library/Hasql/Private/Errors.hs
@@ -1,147 +0,0 @@--- |--- An API for retrieval of multiple results.--- Can be used to handle:------ * A single result,------ * Individual results of a multi-statement query--- with the help of "Applicative" and "Monad",------ * Row-by-row fetching.-module Hasql.Private.Errors where--import qualified Data.ByteString.Char8 as BC-import Hasql.Private.Prelude---- |--- An error during the execution of a query.--- Comes packed with the query template and a textual representation of the provided params.-data QueryError- = QueryError ByteString [Text] CommandError- deriving (Show, Eq, Typeable)--instance Exception QueryError where- displayException (QueryError query params commandError) =- let queryContext :: Maybe (ByteString, Int)- queryContext = case commandError of- ClientError _ -> Nothing- ResultError resultError -> case resultError of- ServerError _ message _ _ (Just position) -> Just (message, position)- _ -> Nothing-- -- find the line number and position of the error- findLineAndPos :: ByteString -> Int -> (Int, Int)- findLineAndPos byteString errorPos =- let (_, line, pos) =- BC.foldl'- ( \(total, line, pos) c ->- case total + 1 of- 0 -> (total, line, pos)- cursor- | cursor == errorPos -> (-1, line, pos + 1)- | c == '\n' -> (total + 1, line + 1, 0)- | otherwise -> (total + 1, line, pos + 1)- )- (0, 1, 0)- byteString- in (line, pos)-- formatErrorContext :: ByteString -> ByteString -> Int -> ByteString- formatErrorContext query message errorPos =- let lines = BC.lines query- (lineNum, linePos) = findLineAndPos query errorPos- in BC.unlines (take lineNum lines)- <> BC.replicate (linePos - 1) ' '- <> "^ "- <> message-- prettyQuery :: ByteString- prettyQuery = case queryContext of- Nothing -> query- Just (message, pos) -> formatErrorContext query message pos- in "QueryError!\n"- <> "\n Query:\n"- <> BC.unpack prettyQuery- <> "\n"- <> "\n Params: "- <> show params- <> "\n Error: "- <> case commandError of- ClientError (Just message) -> "Client error: " <> show message- ClientError Nothing -> "Unknown client error"- ResultError resultError -> case resultError of- ServerError code message details hint position ->- "Server error "- <> BC.unpack code- <> ": "- <> BC.unpack message- <> maybe "" (\d -> "\n Details: " <> BC.unpack d) details- <> maybe "" (\h -> "\n Hint: " <> BC.unpack h) hint- UnexpectedResult message -> "Unexpected result: " <> show message- RowError row column rowError ->- "Row error: " <> show row <> ":" <> show column <> " " <> show rowError- UnexpectedAmountOfRows amount ->- "Unexpected amount of rows: " <> show amount---- |--- 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)- | -- |- -- Some error with a command result.- ResultError ResultError- deriving (Show, Eq)---- |--- An error with a command result.-data ResultError- = -- | An error reported by the DB.- ServerError- -- | __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.- ByteString- -- | __Message__. The primary human-readable error message(typically one- -- line). Always present.- ByteString- -- | __Details__. An optional secondary error message carrying more- -- detail about the problem. Might run to multiple lines.- (Maybe ByteString)- -- | __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) rather than hard facts. Might run to- -- multiple lines.- (Maybe ByteString)- -- | __Position__. Error cursor position as an index into the original- -- statement string. Positions are measured in characters not bytes.- (Maybe Int)- | -- |- -- 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 indexes of the row and column.- RowError Int Int RowError- | -- |- -- An unexpected amount of rows.- UnexpectedAmountOfRows Int- deriving (Show, Eq)---- |--- 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- | -- |- -- Appears on the attempt to parse a @NULL@ as some value.- UnexpectedNull- | -- |- -- Appears when a wrong value parser is used.- -- Comes with the error details.- ValueError Text- deriving (Show, Eq)
− library/Hasql/Private/IO.hs
@@ -1,162 +0,0 @@--- |--- An API of low-level IO operations.-module Hasql.Private.IO where--import qualified Database.PostgreSQL.LibPQ as LibPQ-import qualified Hasql.Private.Commands as Commands-import qualified Hasql.Private.Decoders.Result as ResultDecoders-import qualified Hasql.Private.Decoders.Results as ResultsDecoders-import qualified Hasql.Private.Encoders.Params as ParamsEncoders-import Hasql.Private.Errors-import Hasql.Private.Prelude-import qualified Hasql.Private.PreparedStatementRegistry as PreparedStatementRegistry--{-# INLINE acquireConnection #-}-acquireConnection :: ByteString -> IO LibPQ.Connection-acquireConnection =- LibPQ.connectdb--{-# INLINE acquirePreparedStatementRegistry #-}-acquirePreparedStatementRegistry :: IO PreparedStatementRegistry.PreparedStatementRegistry-acquirePreparedStatementRegistry =- PreparedStatementRegistry.new--{-# INLINE releaseConnection #-}-releaseConnection :: LibPQ.Connection -> IO ()-releaseConnection connection =- LibPQ.finish connection--{-# INLINE checkConnectionStatus #-}-checkConnectionStatus :: LibPQ.Connection -> IO (Maybe (Maybe ByteString))-checkConnectionStatus c =- do- s <- LibPQ.status c- case s of- LibPQ.ConnectionOk -> return Nothing- _ -> fmap Just (LibPQ.errorMessage c)--{-# INLINE checkServerVersion #-}-checkServerVersion :: LibPQ.Connection -> IO (Maybe Int)-checkServerVersion c =- fmap (mfilter (< 80200) . Just) (LibPQ.serverVersion c)--{-# INLINE getIntegerDatetimes #-}-getIntegerDatetimes :: LibPQ.Connection -> IO Bool-getIntegerDatetimes c =- fmap decodeValue $ LibPQ.parameterStatus c "integer_datetimes"- where- decodeValue =- \case- Just "on" -> True- _ -> False--{-# INLINE initConnection #-}-initConnection :: LibPQ.Connection -> IO ()-initConnection c =- void $ LibPQ.exec c (Commands.asBytes (Commands.setEncodersToUTF8 <> Commands.setMinClientMessagesToWarning))--{-# INLINE getResults #-}-getResults :: LibPQ.Connection -> Bool -> ResultsDecoders.Results a -> IO (Either CommandError a)-getResults connection integerDatetimes decoder =- {-# SCC "getResults" #-}- (<*) <$> get <*> dropRemainders- where- get =- ResultsDecoders.run decoder (integerDatetimes, connection)- dropRemainders =- ResultsDecoders.run ResultsDecoders.dropRemainders (integerDatetimes, connection)--{-# INLINE getPreparedStatementKey #-}-getPreparedStatementKey ::- LibPQ.Connection ->- PreparedStatementRegistry.PreparedStatementRegistry ->- ByteString ->- [LibPQ.Oid] ->- IO (Either CommandError ByteString)-getPreparedStatementKey connection registry template oidList =- {-# SCC "getPreparedStatementKey" #-}- PreparedStatementRegistry.update localKey onNewRemoteKey onOldRemoteKey registry- where- localKey =- PreparedStatementRegistry.LocalKey template wordOIDList- where- wordOIDList =- map (\(LibPQ.Oid x) -> fromIntegral x) oidList- onNewRemoteKey key =- do- sent <- LibPQ.sendPrepare connection key template (mfilter (not . null) (Just oidList))- let resultsDecoder =- if sent- then ResultsDecoders.single ResultDecoders.noResult- else ResultsDecoders.clientError- fmap resultsMapping $ getResults connection undefined resultsDecoder- where- resultsMapping =- \case- Left x -> (False, Left x)- Right _ -> (True, Right key)- onOldRemoteKey key =- pure (pure key)--{-# INLINE checkedSend #-}-checkedSend :: LibPQ.Connection -> IO Bool -> IO (Either CommandError ())-checkedSend connection send =- send >>= \case- False -> fmap (Left . ClientError) $ LibPQ.errorMessage connection- True -> pure (Right ())--{-# INLINE sendPreparedParametricStatement #-}-sendPreparedParametricStatement ::- LibPQ.Connection ->- PreparedStatementRegistry.PreparedStatementRegistry ->- Bool ->- ByteString ->- ParamsEncoders.Params a ->- a ->- IO (Either CommandError ())-sendPreparedParametricStatement connection registry integerDatetimes template (ParamsEncoders.Params (Op encoderOp)) input =- let (oidList, valueAndFormatList) =- let step (oid, format, encoder, _) ~(oidList, bytesAndFormatList) =- (,)- (oid : oidList)- (fmap (\bytes -> (bytes, format)) (encoder integerDatetimes) : bytesAndFormatList)- in foldr step ([], []) (encoderOp input)- in runExceptT $ do- key <- ExceptT $ getPreparedStatementKey connection registry template oidList- ExceptT $ checkedSend connection $ LibPQ.sendQueryPrepared connection key valueAndFormatList LibPQ.Binary--{-# INLINE sendUnpreparedParametricStatement #-}-sendUnpreparedParametricStatement ::- LibPQ.Connection ->- Bool ->- ByteString ->- ParamsEncoders.Params a ->- a ->- IO (Either CommandError ())-sendUnpreparedParametricStatement connection integerDatetimes template (ParamsEncoders.Params (Op encoderOp)) input =- let params =- let step (oid, format, encoder, _) acc =- ((,,) <$> pure oid <*> encoder integerDatetimes <*> pure format) : acc- in foldr step [] (encoderOp input)- in checkedSend connection $ LibPQ.sendQueryParams connection template params LibPQ.Binary--{-# INLINE sendParametricStatement #-}-sendParametricStatement ::- LibPQ.Connection ->- Bool ->- PreparedStatementRegistry.PreparedStatementRegistry ->- ByteString ->- ParamsEncoders.Params a ->- Bool ->- a ->- IO (Either CommandError ())-sendParametricStatement connection integerDatetimes registry template encoder prepared params =- {-# SCC "sendParametricStatement" #-}- if prepared- then sendPreparedParametricStatement connection registry integerDatetimes template encoder params- else sendUnpreparedParametricStatement connection integerDatetimes template encoder params--{-# INLINE sendNonparametricStatement #-}-sendNonparametricStatement :: LibPQ.Connection -> ByteString -> IO (Either CommandError ())-sendNonparametricStatement connection sql =- checkedSend connection $ LibPQ.sendQuery connection sql
− library/Hasql/Private/PTI.hs
@@ -1,230 +0,0 @@-module Hasql.Private.PTI where--import qualified Database.PostgreSQL.LibPQ as LibPQ-import Hasql.Private.Prelude hiding (bool)---- | A Postgresql type info-data PTI = PTI {ptiOID :: !OID, ptiArrayOID :: !(Maybe OID)}---- | A Word32 and a LibPQ representation of an OID-data OID = OID {oidWord32 :: !Word32, oidPQ :: !LibPQ.Oid, oidFormat :: !LibPQ.Format}--mkOID :: LibPQ.Format -> Word32 -> OID-mkOID format x =- OID x ((LibPQ.Oid . fromIntegral) x) format--mkPTI :: LibPQ.Format -> Word32 -> Maybe Word32 -> PTI-mkPTI format oid arrayOID =- PTI (mkOID format oid) (fmap (mkOID format) arrayOID)---- * Constants--abstime :: PTI-abstime = mkPTI LibPQ.Binary 702 (Just 1023)--aclitem :: PTI-aclitem = mkPTI LibPQ.Binary 1033 (Just 1034)--bit :: PTI-bit = mkPTI LibPQ.Binary 1560 (Just 1561)--bool :: PTI-bool = mkPTI LibPQ.Binary 16 (Just 1000)--box :: PTI-box = mkPTI LibPQ.Binary 603 (Just 1020)--bpchar :: PTI-bpchar = mkPTI LibPQ.Binary 1042 (Just 1014)--bytea :: PTI-bytea = mkPTI LibPQ.Binary 17 (Just 1001)--char :: PTI-char = mkPTI LibPQ.Binary 18 (Just 1002)--cid :: PTI-cid = mkPTI LibPQ.Binary 29 (Just 1012)--cidr :: PTI-cidr = mkPTI LibPQ.Binary 650 (Just 651)--circle :: PTI-circle = mkPTI LibPQ.Binary 718 (Just 719)--cstring :: PTI-cstring = mkPTI LibPQ.Binary 2275 (Just 1263)--date :: PTI-date = mkPTI LibPQ.Binary 1082 (Just 1182)--daterange :: PTI-daterange = mkPTI LibPQ.Binary 3912 (Just 3913)--float4 :: PTI-float4 = mkPTI LibPQ.Binary 700 (Just 1021)--float8 :: PTI-float8 = mkPTI LibPQ.Binary 701 (Just 1022)--gtsvector :: PTI-gtsvector = mkPTI LibPQ.Binary 3642 (Just 3644)--inet :: PTI-inet = mkPTI LibPQ.Binary 869 (Just 1041)--int2 :: PTI-int2 = mkPTI LibPQ.Binary 21 (Just 1005)--int2vector :: PTI-int2vector = mkPTI LibPQ.Binary 22 (Just 1006)--int4 :: PTI-int4 = mkPTI LibPQ.Binary 23 (Just 1007)--int4range :: PTI-int4range = mkPTI LibPQ.Binary 3904 (Just 3905)--int8 :: PTI-int8 = mkPTI LibPQ.Binary 20 (Just 1016)--int8range :: PTI-int8range = mkPTI LibPQ.Binary 3926 (Just 3927)--interval :: PTI-interval = mkPTI LibPQ.Binary 1186 (Just 1187)--json :: PTI-json = mkPTI LibPQ.Binary 114 (Just 199)--jsonb :: PTI-jsonb = mkPTI LibPQ.Binary 3802 (Just 3807)--line :: PTI-line = mkPTI LibPQ.Binary 628 (Just 629)--lseg :: PTI-lseg = mkPTI LibPQ.Binary 601 (Just 1018)--macaddr :: PTI-macaddr = mkPTI LibPQ.Binary 829 (Just 1040)--money :: PTI-money = mkPTI LibPQ.Binary 790 (Just 791)--name :: PTI-name = mkPTI LibPQ.Binary 19 (Just 1003)--numeric :: PTI-numeric = mkPTI LibPQ.Binary 1700 (Just 1231)--numrange :: PTI-numrange = mkPTI LibPQ.Binary 3906 (Just 3907)--oid :: PTI-oid = mkPTI LibPQ.Binary 26 (Just 1028)--oidvector :: PTI-oidvector = mkPTI LibPQ.Binary 30 (Just 1013)--path :: PTI-path = mkPTI LibPQ.Binary 602 (Just 1019)--point :: PTI-point = mkPTI LibPQ.Binary 600 (Just 1017)--polygon :: PTI-polygon = mkPTI LibPQ.Binary 604 (Just 1027)--record :: PTI-record = mkPTI LibPQ.Binary 2249 (Just 2287)--refcursor :: PTI-refcursor = mkPTI LibPQ.Binary 1790 (Just 2201)--regclass :: PTI-regclass = mkPTI LibPQ.Binary 2205 (Just 2210)--regconfig :: PTI-regconfig = mkPTI LibPQ.Binary 3734 (Just 3735)--regdictionary :: PTI-regdictionary = mkPTI LibPQ.Binary 3769 (Just 3770)--regoper :: PTI-regoper = mkPTI LibPQ.Binary 2203 (Just 2208)--regoperator :: PTI-regoperator = mkPTI LibPQ.Binary 2204 (Just 2209)--regproc :: PTI-regproc = mkPTI LibPQ.Binary 24 (Just 1008)--regprocedure :: PTI-regprocedure = mkPTI LibPQ.Binary 2202 (Just 2207)--regtype :: PTI-regtype = mkPTI LibPQ.Binary 2206 (Just 2211)--reltime :: PTI-reltime = mkPTI LibPQ.Binary 703 (Just 1024)--text :: PTI-text = mkPTI LibPQ.Binary 25 (Just 1009)--tid :: PTI-tid = mkPTI LibPQ.Binary 27 (Just 1010)--time :: PTI-time = mkPTI LibPQ.Binary 1083 (Just 1183)--timestamp :: PTI-timestamp = mkPTI LibPQ.Binary 1114 (Just 1115)--timestamptz :: PTI-timestamptz = mkPTI LibPQ.Binary 1184 (Just 1185)--timetz :: PTI-timetz = mkPTI LibPQ.Binary 1266 (Just 1270)--tinterval :: PTI-tinterval = mkPTI LibPQ.Binary 704 (Just 1025)--tsquery :: PTI-tsquery = mkPTI LibPQ.Binary 3615 (Just 3645)--tsrange :: PTI-tsrange = mkPTI LibPQ.Binary 3908 (Just 3909)--tstzrange :: PTI-tstzrange = mkPTI LibPQ.Binary 3910 (Just 3911)--tsvector :: PTI-tsvector = mkPTI LibPQ.Binary 3614 (Just 3643)--txid_snapshot :: PTI-txid_snapshot = mkPTI LibPQ.Binary 2970 (Just 2949)--textUnknown :: PTI-textUnknown = mkPTI LibPQ.Text 705 (Just 705)--binaryUnknown :: PTI-binaryUnknown = mkPTI LibPQ.Binary 705 (Just 705)--uuid :: PTI-uuid = mkPTI LibPQ.Binary 2950 (Just 2951)--varbit :: PTI-varbit = mkPTI LibPQ.Binary 1562 (Just 1563)--varchar :: PTI-varchar = mkPTI LibPQ.Binary 1043 (Just 1015)--void :: PTI-void = mkPTI LibPQ.Binary 2278 Nothing--xid :: PTI-xid = mkPTI LibPQ.Binary 28 (Just 1011)--xml :: PTI-xml = mkPTI LibPQ.Binary 142 (Just 143)
− library/Hasql/Private/Prelude.hs
@@ -1,137 +0,0 @@-module Hasql.Private.Prelude- ( module Exports,- LazyByteString,- ByteStringBuilder,- LazyText,- TextBuilder,- forMToZero_,- forMFromZero_,- strictCons,- mapLeft,- )-where--import Control.Applicative as Exports hiding (WrappedArrow (..))-import Control.Arrow as Exports hiding (first, second)-import Control.Category as Exports-import Control.Concurrent as Exports-import Control.Exception as Exports-import Control.Monad as Exports hiding (fail, forM, forM_, mapM, mapM_, msum, sequence, sequence_)-import Control.Monad.Error.Class as Exports (MonadError (..))-import Control.Monad.Fail as Exports-import Control.Monad.Fix as Exports hiding (fix)-import Control.Monad.IO.Class as Exports-import Control.Monad.Reader.Class as Exports (MonadReader (..))-import Control.Monad.ST as Exports-import Control.Monad.Trans.Class as Exports-import Control.Monad.Trans.Cont as Exports hiding (callCC, shift)-import Control.Monad.Trans.Except as Exports (Except, ExceptT (ExceptT), catchE, except, mapExcept, mapExceptT, runExcept, runExceptT, throwE, withExcept, withExceptT)-import Control.Monad.Trans.Maybe as Exports-import Control.Monad.Trans.Reader as Exports (Reader, ReaderT (ReaderT), mapReader, mapReaderT, runReader, runReaderT, withReader, withReaderT)-import Control.Monad.Trans.State.Strict as Exports (State, StateT (StateT), evalState, evalStateT, execState, execStateT, mapState, mapStateT, runState, runStateT, withState, withStateT)-import Control.Monad.Trans.Writer.Strict as Exports (Writer, WriterT (..), execWriter, execWriterT, mapWriter, mapWriterT, runWriter)-import Data.Bifunctor as Exports-import Data.Bits as Exports-import Data.Bool as Exports-import Data.ByteString as Exports (ByteString)-import qualified Data.ByteString.Builder-import qualified Data.ByteString.Lazy-import Data.Char as Exports-import Data.Coerce as Exports-import Data.Complex as Exports-import Data.DList as Exports (DList)-import Data.Data as Exports-import Data.Dynamic as Exports-import Data.Either as Exports-import Data.Fixed as Exports-import Data.Foldable as Exports hiding (toList)-import Data.Function as Exports hiding (id, (.))-import Data.Functor as Exports hiding (unzip)-import Data.Functor.Compose as Exports-import Data.Functor.Contravariant as Exports-import Data.Functor.Contravariant.Divisible as Exports-import Data.Functor.Identity as Exports-import Data.Hashable as Exports (Hashable (..))-import Data.IORef as Exports-import Data.Int as Exports-import Data.Ix as Exports-import Data.List as Exports hiding (all, and, any, concat, concatMap, elem, find, foldl, foldl', foldl1, foldr, foldr1, isSubsequenceOf, mapAccumL, mapAccumR, maximum, maximumBy, minimum, minimumBy, notElem, or, product, sortOn, sum, uncons)-import Data.List.NonEmpty as Exports (NonEmpty (..))-import Data.Maybe as Exports-import Data.Monoid as Exports hiding (Alt, (<>))-import Data.Ord as Exports-import Data.Profunctor.Unsafe as Exports-import Data.Proxy as Exports-import Data.Ratio as Exports-import Data.STRef as Exports-import Data.Scientific as Exports (Scientific)-import Data.Semigroup as Exports hiding (First (..), Last (..))-import Data.String as Exports-import Data.Text as Exports (Text)-import qualified Data.Text.Lazy-import qualified Data.Text.Lazy.Builder-import Data.Time as Exports-import Data.Traversable as Exports-import Data.Tuple as Exports-import Data.UUID as Exports (UUID)-import Data.Unique as Exports-import Data.Vector as Exports (Vector)-import Data.Version as Exports-import Data.Void as Exports-import Data.Word as Exports-import Debug.Trace as Exports-import Foreign.ForeignPtr as Exports-import Foreign.Ptr as Exports-import Foreign.StablePtr as Exports-import Foreign.Storable as Exports-import GHC.Conc as Exports hiding (orElse, threadWaitRead, threadWaitReadSTM, threadWaitWrite, threadWaitWriteSTM, withMVar)-import GHC.Exts as Exports (IsList (..), groupWith, inline, lazy, sortWith)-import GHC.Generics as Exports (Generic)-import GHC.IO.Exception as Exports-import GHC.OverloadedLabels as Exports-import Numeric as Exports-import System.Environment as Exports-import System.Exit as Exports-import System.IO as Exports (Handle, hClose)-import System.IO.Error as Exports-import System.IO.Unsafe as Exports-import System.Mem as Exports-import System.Mem.StableName as Exports-import System.Timeout as Exports-import Text.ParserCombinators.ReadP as Exports (ReadP, readP_to_S, readS_to_P)-import Text.ParserCombinators.ReadPrec as Exports (ReadPrec, readP_to_Prec, readPrec_to_P, readPrec_to_S, readS_to_Prec)-import Text.Printf as Exports (hPrintf, printf)-import Unsafe.Coerce as Exports-import Prelude as Exports hiding (Read, all, and, any, concat, concatMap, elem, fail, foldl, foldl1, foldr, foldr1, id, mapM, mapM_, maximum, minimum, notElem, or, product, sequence, sequence_, sum, (.))--type LazyByteString =- Data.ByteString.Lazy.ByteString--type ByteStringBuilder =- Data.ByteString.Builder.Builder--type LazyText =- Data.Text.Lazy.Text--type TextBuilder =- Data.Text.Lazy.Builder.Builder--{-# INLINE forMToZero_ #-}-forMToZero_ :: (Applicative m) => Int -> (Int -> m a) -> m ()-forMToZero_ !startN f =- ($ pred startN) $ fix $ \loop !n -> if n >= 0 then f n *> loop (pred n) else pure ()--{-# INLINE forMFromZero_ #-}-forMFromZero_ :: (Applicative m) => Int -> (Int -> m a) -> m ()-forMFromZero_ !endN f =- ($ 0) $ fix $ \loop !n -> if n < endN then f n *> loop (succ n) else pure ()--{-# INLINE strictCons #-}-strictCons :: a -> [a] -> [a]-strictCons !a b =- let !c = a : b in c--{-# INLINE mapLeft #-}-mapLeft :: (a -> c) -> Either a b -> Either c b-mapLeft f =- either (Left . f) Right
− library/Hasql/Private/PreparedStatementRegistry.hs
@@ -1,53 +0,0 @@-module Hasql.Private.PreparedStatementRegistry- ( PreparedStatementRegistry,- new,- update,- LocalKey (..),- )-where--import qualified ByteString.StrictBuilder as B-import qualified Data.HashTable.IO as A-import Hasql.Private.Prelude hiding (lookup)--data PreparedStatementRegistry- = PreparedStatementRegistry !(A.BasicHashTable LocalKey ByteString) !(IORef Word)--{-# INLINEABLE new #-}-new :: IO PreparedStatementRegistry-new =- PreparedStatementRegistry <$> A.new <*> newIORef 0--{-# INLINEABLE update #-}-update :: LocalKey -> (ByteString -> IO (Bool, a)) -> (ByteString -> IO a) -> PreparedStatementRegistry -> IO a-update localKey onNewRemoteKey onOldRemoteKey (PreparedStatementRegistry table counter) =- lookup >>= maybe new old- where- lookup =- A.lookup table localKey- new =- readIORef counter >>= onN- where- onN n =- do- (save, result) <- onNewRemoteKey remoteKey- when save $ do- A.insert table localKey remoteKey- writeIORef counter (succ n)- return result- where- remoteKey =- B.builderBytes . B.asciiIntegral $ n- old =- onOldRemoteKey---- |--- Local statement key.-data LocalKey- = LocalKey !ByteString ![Word32]- deriving (Show, Eq)--instance Hashable LocalKey where- {-# INLINE hashWithSalt #-}- hashWithSalt salt (LocalKey template types) =- hashWithSalt salt template
− library/Hasql/Private/Session.hs
@@ -1,65 +0,0 @@-module Hasql.Private.Session where--import qualified Hasql.Private.Connection as Connection-import qualified Hasql.Private.Decoders.Result as Decoders.Result-import qualified Hasql.Private.Decoders.Results as Decoders.Results-import qualified Hasql.Private.Encoders as Encoders-import qualified Hasql.Private.Encoders.Params as Encoders.Params-import Hasql.Private.Errors-import qualified Hasql.Private.IO as IO-import Hasql.Private.Prelude-import qualified Hasql.Statement as Statement---- |--- A batch of actions to be executed in the context of a database connection.-newtype Session a- = Session (ReaderT Connection.Connection (ExceptT QueryError IO) a)- deriving (Functor, Applicative, Monad, MonadError QueryError, MonadIO, MonadReader Connection.Connection)---- |--- Executes a bunch of commands on the provided connection.-run :: Session a -> Connection.Connection -> IO (Either QueryError a)-run (Session impl) connection =- runExceptT- $ runReaderT impl connection---- |--- Possibly a multi-statement query,--- which however cannot be parameterized or prepared,--- nor can any results of it be collected.-sql :: ByteString -> Session ()-sql sql =- Session- $ ReaderT- $ \(Connection.Connection pqConnectionRef integerDatetimes registry) ->- ExceptT- $ fmap (mapLeft (QueryError sql []))- $ withMVar pqConnectionRef- $ \pqConnection -> do- r1 <- IO.sendNonparametricStatement pqConnection sql- r2 <- IO.getResults pqConnection integerDatetimes decoder- return $ r1 *> r2- where- decoder =- Decoders.Results.single Decoders.Result.noResult---- |--- Parameters and a specification of a parametric single-statement query to apply them to.-statement :: params -> Statement.Statement params result -> Session result-statement input (Statement.Statement template (Encoders.Params paramsEncoder) decoder preparable) =- Session- $ ReaderT- $ \(Connection.Connection pqConnectionRef integerDatetimes registry) ->- ExceptT- $ fmap (mapLeft (QueryError template inputReps))- $ withMVar pqConnectionRef- $ \pqConnection -> do- r1 <- IO.sendParametricStatement pqConnection integerDatetimes registry template paramsEncoder preparable input- r2 <- IO.getResults pqConnection integerDatetimes (unsafeCoerce decoder)- return $ r1 *> r2- where- inputReps =- let Encoders.Params.Params (Op encoderOp) = paramsEncoder- step (_, _, _, rendering) acc =- rendering : acc- in foldr step [] (encoderOp input)
− library/Hasql/Private/Settings.hs
@@ -1,39 +0,0 @@-module Hasql.Private.Settings where--import qualified Data.ByteString as B-import qualified Data.ByteString.Builder as BB-import qualified Data.ByteString.Lazy as BL-import Hasql.Private.Prelude---- |--- All settings encoded in a single byte-string according to--- <http://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNSTRING the PostgreSQL format>.-type Settings =- ByteString---- |--- Encode a host, a port, a user, a password and a database into the PostgreSQL settings byte-string.-{-# INLINE settings #-}-settings :: ByteString -> Word16 -> ByteString -> ByteString -> ByteString -> Settings-settings host port user password database =- BL.toStrict- $ BB.toLazyByteString- $ mconcat- $ intersperse (BB.char7 ' ')- $ catMaybes- $ [ mappend (BB.string7 "host=")- . BB.byteString- <$> mfilter (not . B.null) (pure host),- mappend (BB.string7 "port=")- . BB.word16Dec- <$> mfilter (/= 0) (pure port),- mappend (BB.string7 "user=")- . BB.byteString- <$> mfilter (not . B.null) (pure user),- mappend (BB.string7 "password=")- . BB.byteString- <$> mfilter (not . B.null) (pure password),- mappend (BB.string7 "dbname=")- . BB.byteString- <$> mfilter (not . B.null) (pure database)- ]
library/Hasql/Session.hs view
@@ -7,9 +7,9 @@ run, -- * Errors- module Hasql.Private.Errors,+ module Hasql.Errors, ) where -import Hasql.Private.Errors-import Hasql.Private.Session+import Hasql.Errors+import Hasql.Session.Core
+ library/Hasql/Session/Core.hs view
@@ -0,0 +1,65 @@+module Hasql.Session.Core where++import qualified Hasql.Connection.Core as Connection+import qualified Hasql.Decoders.Result as Decoders.Result+import qualified Hasql.Decoders.Results as Decoders.Results+import qualified Hasql.Encoders.All as Encoders+import qualified Hasql.Encoders.Params as Encoders.Params+import Hasql.Errors+import qualified Hasql.IO as IO+import Hasql.Prelude+import qualified Hasql.Statement as Statement++-- |+-- A batch of actions to be executed in the context of a database connection.+newtype Session a+ = Session (ReaderT Connection.Connection (ExceptT QueryError IO) a)+ deriving (Functor, Applicative, Monad, MonadError QueryError, MonadIO, MonadReader Connection.Connection)++-- |+-- Executes a bunch of commands on the provided connection.+run :: Session a -> Connection.Connection -> IO (Either QueryError a)+run (Session impl) connection =+ runExceptT+ $ runReaderT impl connection++-- |+-- Possibly a multi-statement query,+-- which however cannot be parameterized or prepared,+-- nor can any results of it be collected.+sql :: ByteString -> Session ()+sql sql =+ Session+ $ ReaderT+ $ \(Connection.Connection pqConnectionRef integerDatetimes registry) ->+ ExceptT+ $ fmap (mapLeft (QueryError sql []))+ $ withMVar pqConnectionRef+ $ \pqConnection -> do+ r1 <- IO.sendNonparametricStatement pqConnection sql+ r2 <- IO.getResults pqConnection integerDatetimes decoder+ return $ r1 *> r2+ where+ decoder =+ Decoders.Results.single Decoders.Result.noResult++-- |+-- Parameters and a specification of a parametric single-statement query to apply them to.+statement :: params -> Statement.Statement params result -> Session result+statement input (Statement.Statement template (Encoders.Params paramsEncoder) decoder preparable) =+ Session+ $ ReaderT+ $ \(Connection.Connection pqConnectionRef integerDatetimes registry) ->+ ExceptT+ $ fmap (mapLeft (QueryError template inputReps))+ $ withMVar pqConnectionRef+ $ \pqConnection -> do+ r1 <- IO.sendParametricStatement pqConnection integerDatetimes registry template paramsEncoder preparable input+ r2 <- IO.getResults pqConnection integerDatetimes (unsafeCoerce decoder)+ return $ r1 *> r2+ where+ inputReps =+ let Encoders.Params.Params (Op encoderOp) = paramsEncoder+ step (_, _, _, rendering) acc =+ rendering : acc+ in foldr step [] (encoderOp input)
+ library/Hasql/Settings.hs view
@@ -0,0 +1,39 @@+module Hasql.Settings where++import qualified Data.ByteString as B+import qualified Data.ByteString.Builder as BB+import qualified Data.ByteString.Lazy as BL+import Hasql.Prelude++-- |+-- All settings encoded in a single byte-string according to+-- <http://www.postgresql.org/docs/9.4/static/libpq-connect.html#LIBPQ-CONNSTRING the PostgreSQL format>.+type Settings =+ ByteString++-- |+-- Encode a host, a port, a user, a password and a database into the PostgreSQL settings byte-string.+{-# INLINE settings #-}+settings :: ByteString -> Word16 -> ByteString -> ByteString -> ByteString -> Settings+settings host port user password database =+ BL.toStrict+ $ BB.toLazyByteString+ $ mconcat+ $ intersperse (BB.char7 ' ')+ $ catMaybes+ $ [ mappend (BB.string7 "host=")+ . BB.byteString+ <$> mfilter (not . B.null) (pure host),+ mappend (BB.string7 "port=")+ . BB.word16Dec+ <$> mfilter (/= 0) (pure port),+ mappend (BB.string7 "user=")+ . BB.byteString+ <$> mfilter (not . B.null) (pure user),+ mappend (BB.string7 "password=")+ . BB.byteString+ <$> mfilter (not . B.null) (pure password),+ mappend (BB.string7 "dbname=")+ . BB.byteString+ <$> mfilter (not . B.null) (pure database)+ ]
library/Hasql/Statement.hs view
@@ -13,9 +13,9 @@ where import qualified Hasql.Decoders as Decoders+import qualified Hasql.Decoders.All as Decoders import qualified Hasql.Encoders as Encoders-import qualified Hasql.Private.Decoders as Decoders-import Hasql.Private.Prelude+import Hasql.Prelude -- | -- Specification of a strictly single-statement query, which can be parameterized and prepared.