This commit is contained in:
nikita-volkov 2023-10-12 23:24:12 +00:00 committed by github-actions[bot]
parent 8c2aacc464
commit 55dc24bbd5
17 changed files with 568 additions and 533 deletions

View File

@ -25,7 +25,7 @@ main =
sessionBench "manySmallResults" sessionWithManySmallResults
]
where
sessionBench :: NFData a => String -> B.Session a -> Benchmark
sessionBench :: (NFData a) => String -> B.Session a -> Benchmark
sessionBench name session =
bench name (nfIO (fmap (either (error "") id) (B.run session connection)))

View File

@ -306,7 +306,7 @@ refine fn (Value v) = Value (Value.Value (\b -> A.refine fn (Value.run v b)))
-- x = hstore 'replicateM'
-- @
{-# INLINEABLE hstore #-}
hstore :: (forall m. Monad m => Int -> m (Text, Maybe Text) -> m a) -> Value a
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)))
-- |
@ -348,7 +348,7 @@ listArray = array . dimension 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 :: (GenericVector.Vector vector element) => NullableOrNot Value element -> Value (vector element)
vectorArray = array . dimension GenericVector.replicateM . element
-- |
@ -383,7 +383,7 @@ newtype Array a = Array (Array.Array a)
--
-- * 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 :: (forall m. (Monad m) => Int -> m a -> m b) -> Array a -> Array b
dimension replicateM (Array imp) = Array (Array.dimension replicateM imp)
-- |

View File

@ -13,7 +13,7 @@ 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 :: (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)

View File

@ -34,10 +34,12 @@ rowsAffected =
checkExecStatus $ \case
LibPQ.CommandOk -> True
_ -> False
Result $
ReaderT $ \(_, result) ->
ExceptT $
LibPQ.cmdTuples result & fmap cmdTuplesReader
Result
$ ReaderT
$ \(_, result) ->
ExceptT
$ LibPQ.cmdTuples result
& fmap cmdTuplesReader
where
cmdTuplesReader =
notNothing >=> notEmpty >=> decimal
@ -49,8 +51,8 @@ rowsAffected =
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
mapLeft (\m -> UnexpectedResult ("Decimal parsing failure: " <> fromString m))
$ Attoparsec.parseOnly (Attoparsec.decimal <* Attoparsec.endOfInput) bytes
{-# INLINE checkExecStatus #-}
checkExecStatus :: (LibPQ.ExecStatus -> Bool) -> Result ()
@ -69,14 +71,15 @@ checkExecStatus predicate =
{-# INLINE serverError #-}
serverError :: Result ()
serverError =
Result $
ReaderT $ \(_, result) -> ExceptT $ do
Result
$ ReaderT
$ \(_, result) -> ExceptT $ do
code <-
fmap fold $
LibPQ.resultErrorField result LibPQ.DiagSqlstate
fmap fold
$ LibPQ.resultErrorField result LibPQ.DiagSqlstate
message <-
fmap fold $
LibPQ.resultErrorField result LibPQ.DiagMessagePrimary
fmap fold
$ LibPQ.resultErrorField result LibPQ.DiagMessagePrimary
detail <-
LibPQ.resultErrorField result LibPQ.DiagMessageDetail
hint <-
@ -99,8 +102,9 @@ maybe rowDec =
checkExecStatus $ \case
LibPQ.TuplesOk -> True
_ -> False
Result $
ReaderT $ \(integerDatetimes, result) -> ExceptT $ do
Result
$ ReaderT
$ \(integerDatetimes, result) -> ExceptT $ do
maxRows <- LibPQ.ntuples result
case maxRows of
0 -> return (Right Nothing)
@ -122,8 +126,9 @@ single rowDec =
checkExecStatus $ \case
LibPQ.TuplesOk -> True
_ -> False
Result $
ReaderT $ \(integerDatetimes, result) -> ExceptT $ do
Result
$ ReaderT
$ \(integerDatetimes, result) -> ExceptT $ do
maxRows <- LibPQ.ntuples result
case maxRows of
1 -> do
@ -144,8 +149,9 @@ vector rowDec =
checkExecStatus $ \case
LibPQ.TuplesOk -> True
_ -> False
Result $
ReaderT $ \(integerDatetimes, result) -> ExceptT $ do
Result
$ ReaderT
$ \(integerDatetimes, result) -> ExceptT $ do
maxRows <- LibPQ.ntuples result
maxCols <- LibPQ.nfields result
mvector <- MutableVector.unsafeNew (rowToInt maxRows)
@ -172,10 +178,11 @@ foldl step init rowDec =
checkExecStatus $ \case
LibPQ.TuplesOk -> True
_ -> False
Result $
ReaderT $ \(integerDatetimes, result) ->
ExceptT $
{-# SCC "traversal" #-}
Result
$ ReaderT
$ \(integerDatetimes, result) ->
ExceptT
$ {-# SCC "traversal" #-}
do
maxRows <- LibPQ.ntuples result
maxCols <- LibPQ.nfields result
@ -203,8 +210,9 @@ foldr step init rowDec =
checkExecStatus $ \case
LibPQ.TuplesOk -> True
_ -> False
Result $
ReaderT $ \(integerDatetimes, result) -> ExceptT $ do
Result
$ ReaderT
$ \(integerDatetimes, result) -> ExceptT $ do
maxRows <- LibPQ.ntuples result
maxCols <- LibPQ.nfields result
accRef <- newIORef init

View File

@ -29,18 +29,20 @@ run (Results stack) env =
{-# INLINE clientError #-}
clientError :: Results a
clientError =
Results $
ReaderT $ \(_, connection) ->
ExceptT $
fmap (Left . ClientError) (LibPQ.errorMessage connection)
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
Results
$ ReaderT
$ \(integerDatetimes, connection) -> ExceptT $ do
resultMaybe <- LibPQ.getResult connection
case resultMaybe of
Just result ->
@ -53,8 +55,9 @@ single resultDec =
{-# INLINE getResult #-}
getResult :: Results LibPQ.Result
getResult =
Results $
ReaderT $ \(_, connection) -> ExceptT $ do
Results
$ ReaderT
$ \(_, connection) -> ExceptT $ do
resultMaybe <- LibPQ.getResult connection
case resultMaybe of
Just result -> pure (Right result)
@ -85,7 +88,8 @@ dropRemainders =
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
refine refiner results = Results
$ ReaderT
$ \env -> ExceptT $ do
resultEither <- run results env
return $ resultEither >>= mapLeft (ResultError . UnexpectedResult) . refiner

View File

@ -41,21 +41,22 @@ error x =
value :: Value.Value a -> Row (Maybe a)
value valueDec =
{-# SCC "value" #-}
Row $
ReaderT $ \(Env result row columnsAmount integerDatetimes columnRef) -> ExceptT $ do
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
pure
$ case valueMaybe of
Nothing ->
Right Nothing
Just value ->
fmap Just $
mapLeft ValueError $
{-# SCC "decode" #-} A.valueParser (Value.run valueDec integerDatetimes) value
fmap Just
$ mapLeft ValueError
$ {-# SCC "decode" #-} A.valueParser (Value.run valueDec integerDatetimes) value
else pure (Left EndOfInput)
-- |

View File

@ -330,7 +330,7 @@ composite (Composite encode print) =
-- 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 :: (Foldable foldable) => NullableOrNot Value element -> Value (foldable element)
foldableArray = array . dimension foldl' . element
-- * Array

View File

@ -19,8 +19,9 @@ value =
nullableValue :: C.Value a -> Params (Maybe a)
nullableValue (C.Value valueOID arrayOID encode render) =
Params $
Op $ \input ->
Params
$ Op
$ \input ->
let D.OID _ pqOid format =
valueOID
encoder env =

View File

@ -19,6 +19,6 @@ 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 :: (Show a) => PTI.PTI -> (Bool -> a -> B.Encoding) -> Value a
unsafePTIWithShow pti encode =
unsafePTI pti encode (C.string . show)

View File

@ -39,24 +39,24 @@ data CommandError
data ResultError
= -- | An error reported by the DB.
ServerError
ByteString
-- ^ __Code__. The SQLSTATE code for the error. It's recommended to use
-- | __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
-- | __Message__. The primary human-readable error message(typically one
-- line). Always present.
(Maybe ByteString)
-- ^ __Details__. An optional secondary error message carrying more
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.
-- | __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 Int)
-- ^ __Position__. Error cursor position as an index into the original
(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.

View File

@ -117,12 +117,12 @@ type TextBuilder =
Data.Text.Lazy.Builder.Builder
{-# INLINE forMToZero_ #-}
forMToZero_ :: Applicative m => Int -> (Int -> m a) -> m ()
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_ :: (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 ()

View File

@ -22,8 +22,8 @@ newtype Session a
-- 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
runExceptT
$ runReaderT impl connection
-- |
-- Possibly a multi-statement query,
@ -31,14 +31,16 @@ run (Session impl) connection =
-- 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
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
@ -47,14 +49,16 @@ sql sql =
-- 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
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

View File

@ -16,19 +16,24 @@ type Settings =
{-# 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)
]
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)
]

View File

@ -20,141 +20,147 @@ main =
defaultMain tree
tree =
localOption (NumThreads 1) $
testGroup
localOption (NumThreads 1)
$ testGroup
"All tests"
[ testGroup "Roundtrips" $
let roundtrip encoder decoder input =
let session =
let statement = Statement.Statement "select $1" encoder decoder True
in Session.statement input statement
in unsafePerformIO $ do
x <- Connection.with (Session.run session)
return (Right (Right input) === x)
in [ testProperty "Array" $
let encoder = Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8)))))
decoder = Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.array (Decoders.dimension replicateM (Decoders.element (Decoders.nonNullable Decoders.int8))))))
in roundtrip encoder decoder,
testProperty "2D Array" $
let encoder = Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8))))))
decoder = Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.array (Decoders.dimension replicateM (Decoders.dimension replicateM (Decoders.element (Decoders.nonNullable Decoders.int8)))))))
in \list -> list /= [] ==> roundtrip encoder decoder (replicate 3 list)
],
testCase "Failed query" $
let statement =
Statement.Statement "select true where 1 = any ($1) and $2" encoder decoder True
where
encoder =
contrazip2
(Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8))))))
(Encoders.param (Encoders.nonNullable (Encoders.text)))
decoder =
fmap (maybe False (const True)) (Decoders.rowMaybe ((Decoders.column . Decoders.nonNullable) Decoders.bool))
session =
Session.statement ([3, 7], "a") statement
in do
x <- Connection.with (Session.run session)
assertBool (show x) $ case x of
Right (Left (Session.QueryError "select true where 1 = any ($1) and $2" ["[3, 7]", "\"a\""] _)) -> True
_ -> False,
testCase "IN simulation" $
let statement =
Statement.Statement "select true where 1 = any ($1)" encoder decoder True
where
encoder =
Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8)))))
decoder =
fmap (maybe False (const True)) (Decoders.rowMaybe ((Decoders.column . Decoders.nonNullable) Decoders.bool))
session =
do
result1 <- Session.statement [1, 2] statement
result2 <- Session.statement [2, 3] statement
return (result1, result2)
in do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right (True, False))) x,
testCase "NOT IN simulation" $
let statement =
Statement.Statement "select true where 3 <> all ($1)" encoder decoder True
where
encoder =
Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8)))))
decoder =
fmap (maybe False (const True)) (Decoders.rowMaybe ((Decoders.column . Decoders.nonNullable) Decoders.bool))
session =
do
result1 <- Session.statement [1, 2] statement
result2 <- Session.statement [2, 3] statement
return (result1, result2)
in do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right (True, False))) x,
testCase "Composite decoding" $
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select (1, true)"
encoder =
mempty
decoder =
Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.composite ((,) <$> (Decoders.field . Decoders.nonNullable) Decoders.int8 <*> (Decoders.field . Decoders.nonNullable) Decoders.bool)))
session =
Session.statement () statement
in do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right (1, True))) x,
testCase "Complex composite decoding" $
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select (1, true) as entity1, ('hello', 3) as entity2"
encoder =
mempty
decoder =
Decoders.singleRow $
(,) <$> (Decoders.column . Decoders.nonNullable) entity1 <*> (Decoders.column . Decoders.nonNullable) entity2
where
entity1 =
Decoders.composite $
(,) <$> (Decoders.field . Decoders.nonNullable) Decoders.int8 <*> (Decoders.field . Decoders.nonNullable) Decoders.bool
entity2 =
Decoders.composite $
(,) <$> (Decoders.field . Decoders.nonNullable) Decoders.text <*> (Decoders.field . Decoders.nonNullable) Decoders.int8
session =
Session.statement () statement
in do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right ((1, True), ("hello", 3)))) x,
testGroup "unknownEnum" $
[ testCase "" $ do
res <- DSL.session $ do
let statement =
Statement.Statement sql mempty Decoders.noResult True
[ testGroup "Roundtrips"
$ let roundtrip encoder decoder input =
let session =
let statement = Statement.Statement "select $1" encoder decoder True
in Session.statement input statement
in unsafePerformIO $ do
x <- Connection.with (Session.run session)
return (Right (Right input) === x)
in [ testProperty "Array"
$ let encoder = Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8)))))
decoder = Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.array (Decoders.dimension replicateM (Decoders.element (Decoders.nonNullable Decoders.int8))))))
in roundtrip encoder decoder,
testProperty "2D Array"
$ let encoder = Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8))))))
decoder = Decoders.singleRow (Decoders.column (Decoders.nonNullable (Decoders.array (Decoders.dimension replicateM (Decoders.dimension replicateM (Decoders.element (Decoders.nonNullable Decoders.int8)))))))
in \list -> list /= [] ==> roundtrip encoder decoder (replicate 3 list)
],
testCase "Failed query"
$ let statement =
Statement.Statement "select true where 1 = any ($1) and $2" encoder decoder True
where
encoder =
contrazip2
(Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8))))))
(Encoders.param (Encoders.nonNullable (Encoders.text)))
decoder =
fmap (maybe False (const True)) (Decoders.rowMaybe ((Decoders.column . Decoders.nonNullable) Decoders.bool))
session =
Session.statement ([3, 7], "a") statement
in do
x <- Connection.with (Session.run session)
assertBool (show x) $ case x of
Right (Left (Session.QueryError "select true where 1 = any ($1) and $2" ["[3, 7]", "\"a\""] _)) -> True
_ -> False,
testCase "IN simulation"
$ let statement =
Statement.Statement "select true where 1 = any ($1)" encoder decoder True
where
encoder =
Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8)))))
decoder =
fmap (maybe False (const True)) (Decoders.rowMaybe ((Decoders.column . Decoders.nonNullable) Decoders.bool))
session =
do
result1 <- Session.statement [1, 2] statement
result2 <- Session.statement [2, 3] statement
return (result1, result2)
in do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right (True, False))) x,
testCase "NOT IN simulation"
$ let statement =
Statement.Statement "select true where 3 <> all ($1)" encoder decoder True
where
encoder =
Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8)))))
decoder =
fmap (maybe False (const True)) (Decoders.rowMaybe ((Decoders.column . Decoders.nonNullable) Decoders.bool))
session =
do
result1 <- Session.statement [1, 2] statement
result2 <- Session.statement [2, 3] statement
return (result1, result2)
in do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right (True, False))) x,
testCase "Composite decoding"
$ let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select (1, true)"
encoder =
mempty
decoder =
Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.composite ((,) <$> (Decoders.field . Decoders.nonNullable) Decoders.int8 <*> (Decoders.field . Decoders.nonNullable) Decoders.bool)))
session =
Session.statement () statement
in do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right (1, True))) x,
testCase "Complex composite decoding"
$ let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select (1, true) as entity1, ('hello', 3) as entity2"
encoder =
mempty
decoder =
Decoders.singleRow
$ (,)
<$> (Decoders.column . Decoders.nonNullable) entity1
<*> (Decoders.column . Decoders.nonNullable) entity2
where
sql =
"drop type if exists mood"
in DSL.statement () statement
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create type mood as enum ('sad', 'ok', 'happy')"
in DSL.statement () statement
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.enum (Just . id))))
encoder =
Encoders.param (Encoders.nonNullable (Encoders.unknownEnum id))
in DSL.statement "ok" statement
entity1 =
Decoders.composite
$ (,)
<$> (Decoders.field . Decoders.nonNullable) Decoders.int8
<*> (Decoders.field . Decoders.nonNullable) Decoders.bool
entity2 =
Decoders.composite
$ (,)
<$> (Decoders.field . Decoders.nonNullable) Decoders.text
<*> (Decoders.field . Decoders.nonNullable) Decoders.int8
session =
Session.statement () statement
in do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right ((1, True), ("hello", 3)))) x,
testGroup "unknownEnum"
$ [ testCase "" $ do
res <- DSL.session $ do
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"drop type if exists mood"
in DSL.statement () statement
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create type mood as enum ('sad', 'ok', 'happy')"
in DSL.statement () statement
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.enum (Just . id))))
encoder =
Encoders.param (Encoders.nonNullable (Encoders.unknownEnum id))
in DSL.statement "ok" statement
assertEqual "" (Right "ok") res
],
assertEqual "" (Right "ok") res
],
testCase "Composite encoding" $ do
let value =
(123, 456, 789, "abc")
@ -165,15 +171,18 @@ tree =
sql =
"select $1 :: pg_enum"
encoder =
Encoders.param . Encoders.nonNullable . Encoders.composite . mconcat $
[ contramap (\(a, _, _, _) -> a) . Encoders.field . Encoders.nonNullable $ Encoders.oid,
contramap (\(_, a, _, _) -> a) . Encoders.field . Encoders.nonNullable $ Encoders.oid,
contramap (\(_, _, a, _) -> a) . Encoders.field . Encoders.nonNullable $ Encoders.float4,
contramap (\(_, _, _, a) -> a) . Encoders.field . Encoders.nonNullable $ Encoders.name
]
Encoders.param
. Encoders.nonNullable
. Encoders.composite
. mconcat
$ [ contramap (\(a, _, _, _) -> a) . Encoders.field . Encoders.nonNullable $ Encoders.oid,
contramap (\(_, a, _, _) -> a) . Encoders.field . Encoders.nonNullable $ Encoders.oid,
contramap (\(_, _, a, _) -> a) . Encoders.field . Encoders.nonNullable $ Encoders.float4,
contramap (\(_, _, _, a) -> a) . Encoders.field . Encoders.nonNullable $ Encoders.name
]
decoder =
Decoders.singleRow $
(Decoders.column . Decoders.nonNullable . Decoders.composite)
Decoders.singleRow
$ (Decoders.column . Decoders.nonNullable . Decoders.composite)
( (,,,)
<$> (Decoders.field . Decoders.nonNullable) Decoders.int4
<*> (Decoders.field . Decoders.nonNullable) Decoders.int4
@ -182,270 +191,270 @@ tree =
)
in Connection.with $ Session.run $ Session.statement value statement
assertEqual "" (Right (Right value)) res,
testCase "Empty array" $
let io =
do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right [])) x
where
session =
Session.statement () statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"select array[]::int8[]"
encoder =
mempty
decoder =
Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.array (Decoders.dimension replicateM (Decoders.element (Decoders.nonNullable Decoders.int8)))))
in io,
testCase "Failing prepared statements" $
let io =
Connection.with (Session.run session)
>>= (assertBool <$> show <*> resultTest)
where
resultTest =
\case
Right (Left (Session.QueryError _ _ (Session.ResultError (Session.ServerError "26000" _ _ _ _)))) -> False
_ -> True
session =
catchError session (const (pure ())) *> session
where
session =
Session.statement () statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"absurd"
encoder =
mempty
decoder =
Decoders.noResult
in io,
testCase "Prepared statements after error" $
let io =
Connection.with (Session.run session)
>>= \x -> assertBool (show x) (either (const False) isRight x)
where
session =
try *> fail *> try
where
try =
Session.statement 1 statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1 :: int8"
encoder =
Encoders.param (Encoders.nonNullable (Encoders.int8))
decoder =
Decoders.singleRow $ (Decoders.column . Decoders.nonNullable) Decoders.int8
fail =
catchError (Session.sql "absurd") (const (pure ()))
in io,
testCase "\"in progress after error\" bugfix" $
let sumStatement :: Statement.Statement (Int64, Int64) Int64
sumStatement =
Statement.Statement sql encoder decoder True
where
sql =
"select ($1 + $2)"
encoder =
contramap fst (Encoders.param (Encoders.nonNullable (Encoders.int8)))
<> contramap snd (Encoders.param (Encoders.nonNullable (Encoders.int8)))
decoder =
Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int8)
sumSession :: Session.Session Int64
sumSession =
Session.sql "begin" *> Session.statement (1, 1) sumStatement <* Session.sql "end"
errorSession :: Session.Session ()
errorSession =
Session.sql "asldfjsldk"
io =
Connection.with $ \c -> do
Session.run errorSession c
Session.run sumSession c
in io >>= \x -> assertBool (show x) (either (const False) isRight x),
testCase "\"another command is already in progress\" bugfix" $
let sumStatement :: Statement.Statement (Int64, Int64) Int64
sumStatement =
Statement.Statement sql encoder decoder True
where
sql =
"select ($1 + $2)"
encoder =
contramap fst (Encoders.param (Encoders.nonNullable (Encoders.int8)))
<> contramap snd (Encoders.param (Encoders.nonNullable (Encoders.int8)))
decoder =
Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int8)
session :: Session.Session Int64
session =
do
Session.sql "begin;"
s <- Session.statement (1, 1) sumStatement
Session.sql "end;"
return s
in DSL.session session >>= \x -> assertEqual (show x) (Right 2) x,
testCase "Executing the same query twice" $
pure (),
testCase "Interval Encoding" $
let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1 = interval '10 seconds'"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.bool)))
encoder =
Encoders.param (Encoders.nonNullable (Encoders.interval))
in DSL.statement (10 :: DiffTime) statement
in actualIO >>= \x -> assertEqual (show x) (Right True) x,
testCase "Interval Decoding" $
let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select interval '10 seconds'"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.interval)))
encoder =
Encoders.noParams
in DSL.statement () statement
in actualIO >>= \x -> assertEqual (show x) (Right (10 :: DiffTime)) x,
testCase "Interval Encoding/Decoding" $
let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.interval)))
encoder =
Encoders.param (Encoders.nonNullable (Encoders.interval))
in DSL.statement (10 :: DiffTime) statement
in actualIO >>= \x -> assertEqual (show x) (Right (10 :: DiffTime)) x,
testCase "Unknown" $
let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"drop type if exists mood"
in DSL.statement () statement
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create type mood as enum ('sad', 'ok', 'happy')"
in DSL.statement () statement
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1 = ('ok' :: mood)"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.bool)))
encoder =
Encoders.param (Encoders.nonNullable (Encoders.unknown))
in DSL.statement "ok" statement
in actualIO >>= assertEqual "" (Right True),
testCase "Textual Unknown" $
let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create or replace function overloaded(a int, b int) returns int as $$ select a + b $$ language sql;"
in DSL.statement () statement
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create or replace function overloaded(a text, b text, c text) returns text as $$ select a || b || c $$ language sql;"
in DSL.statement () statement
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select overloaded($1, $2) || overloaded($3, $4, $5)"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.text)))
encoder =
contramany (Encoders.param (Encoders.nonNullable (Encoders.unknown)))
in DSL.statement ["1", "2", "4", "5", "6"] statement
in actualIO >>= assertEqual "" (Right "3456"),
testCase "Enum" $
let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"drop type if exists mood"
in DSL.statement () statement
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create type mood as enum ('sad', 'ok', 'happy')"
in DSL.statement () statement
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select ($1 :: mood)"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.enum (Just . id))))
encoder =
Encoders.param (Encoders.nonNullable ((Encoders.enum id)))
in DSL.statement "ok" statement
in actualIO >>= assertEqual "" (Right "ok"),
testCase "The same prepared statement used on different types" $
let actualIO =
DSL.session $ do
let effect1 =
DSL.statement "ok" statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1"
encoder =
Encoders.param (Encoders.nonNullable (Encoders.text))
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.text)))
effect2 =
DSL.statement 1 statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1"
encoder =
Encoders.param (Encoders.nonNullable (Encoders.int8))
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int8))
in (,) <$> effect1 <*> effect2
in actualIO >>= assertEqual "" (Right ("ok", 1)),
testCase "Affected rows counting" $
replicateM_ 13 $
let actualIO =
testCase "Empty array"
$ let io =
do
x <- Connection.with (Session.run session)
assertEqual (show x) (Right (Right [])) x
where
session =
Session.statement () statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"select array[]::int8[]"
encoder =
mempty
decoder =
Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.array (Decoders.dimension replicateM (Decoders.element (Decoders.nonNullable Decoders.int8)))))
in io,
testCase "Failing prepared statements"
$ let io =
Connection.with (Session.run session)
>>= (assertBool <$> show <*> resultTest)
where
resultTest =
\case
Right (Left (Session.QueryError _ _ (Session.ResultError (Session.ServerError "26000" _ _ _ _)))) -> False
_ -> True
session =
catchError session (const (pure ())) *> session
where
session =
Session.statement () statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"absurd"
encoder =
mempty
decoder =
Decoders.noResult
in io,
testCase "Prepared statements after error"
$ let io =
Connection.with (Session.run session)
>>= \x -> assertBool (show x) (either (const False) isRight x)
where
session =
try *> fail *> try
where
try =
Session.statement 1 statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1 :: int8"
encoder =
Encoders.param (Encoders.nonNullable (Encoders.int8))
decoder =
Decoders.singleRow $ (Decoders.column . Decoders.nonNullable) Decoders.int8
fail =
catchError (Session.sql "absurd") (const (pure ()))
in io,
testCase "\"in progress after error\" bugfix"
$ let sumStatement :: Statement.Statement (Int64, Int64) Int64
sumStatement =
Statement.Statement sql encoder decoder True
where
sql =
"select ($1 + $2)"
encoder =
contramap fst (Encoders.param (Encoders.nonNullable (Encoders.int8)))
<> contramap snd (Encoders.param (Encoders.nonNullable (Encoders.int8)))
decoder =
Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int8)
sumSession :: Session.Session Int64
sumSession =
Session.sql "begin" *> Session.statement (1, 1) sumStatement <* Session.sql "end"
errorSession :: Session.Session ()
errorSession =
Session.sql "asldfjsldk"
io =
Connection.with $ \c -> do
Session.run errorSession c
Session.run sumSession c
in io >>= \x -> assertBool (show x) (either (const False) isRight x),
testCase "\"another command is already in progress\" bugfix"
$ let sumStatement :: Statement.Statement (Int64, Int64) Int64
sumStatement =
Statement.Statement sql encoder decoder True
where
sql =
"select ($1 + $2)"
encoder =
contramap fst (Encoders.param (Encoders.nonNullable (Encoders.int8)))
<> contramap snd (Encoders.param (Encoders.nonNullable (Encoders.int8)))
decoder =
Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int8)
session :: Session.Session Int64
session =
do
Session.sql "begin;"
s <- Session.statement (1, 1) sumStatement
Session.sql "end;"
return s
in DSL.session session >>= \x -> assertEqual (show x) (Right 2) x,
testCase "Executing the same query twice"
$ pure (),
testCase "Interval Encoding"
$ let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1 = interval '10 seconds'"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.bool)))
encoder =
Encoders.param (Encoders.nonNullable (Encoders.interval))
in DSL.statement (10 :: DiffTime) statement
in actualIO >>= \x -> assertEqual (show x) (Right True) x,
testCase "Interval Decoding"
$ let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select interval '10 seconds'"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.interval)))
encoder =
Encoders.noParams
in DSL.statement () statement
in actualIO >>= \x -> assertEqual (show x) (Right (10 :: DiffTime)) x,
testCase "Interval Encoding/Decoding"
$ let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.interval)))
encoder =
Encoders.param (Encoders.nonNullable (Encoders.interval))
in DSL.statement (10 :: DiffTime) statement
in actualIO >>= \x -> assertEqual (show x) (Right (10 :: DiffTime)) x,
testCase "Unknown"
$ let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"drop type if exists mood"
in DSL.statement () statement
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create type mood as enum ('sad', 'ok', 'happy')"
in DSL.statement () statement
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1 = ('ok' :: mood)"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.bool)))
encoder =
Encoders.param (Encoders.nonNullable (Encoders.unknown))
in DSL.statement "ok" statement
in actualIO >>= assertEqual "" (Right True),
testCase "Textual Unknown"
$ let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create or replace function overloaded(a int, b int) returns int as $$ select a + b $$ language sql;"
in DSL.statement () statement
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create or replace function overloaded(a text, b text, c text) returns text as $$ select a || b || c $$ language sql;"
in DSL.statement () statement
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select overloaded($1, $2) || overloaded($3, $4, $5)"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.text)))
encoder =
contramany (Encoders.param (Encoders.nonNullable (Encoders.unknown)))
in DSL.statement ["1", "2", "4", "5", "6"] statement
in actualIO >>= assertEqual "" (Right "3456"),
testCase "Enum"
$ let actualIO =
DSL.session $ do
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"drop type if exists mood"
in DSL.statement () statement
let statement =
Statement.Statement sql mempty Decoders.noResult True
where
sql =
"create type mood as enum ('sad', 'ok', 'happy')"
in DSL.statement () statement
let statement =
Statement.Statement sql encoder decoder True
where
sql =
"select ($1 :: mood)"
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.enum (Just . id))))
encoder =
Encoders.param (Encoders.nonNullable ((Encoders.enum id)))
in DSL.statement "ok" statement
in actualIO >>= assertEqual "" (Right "ok"),
testCase "The same prepared statement used on different types"
$ let actualIO =
DSL.session $ do
let effect1 =
DSL.statement "ok" statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1"
encoder =
Encoders.param (Encoders.nonNullable (Encoders.text))
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) (Decoders.text)))
effect2 =
DSL.statement 1 statement
where
statement =
Statement.Statement sql encoder decoder True
where
sql =
"select $1"
encoder =
Encoders.param (Encoders.nonNullable (Encoders.int8))
decoder =
(Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int8))
in (,) <$> effect1 <*> effect2
in actualIO >>= assertEqual "" (Right ("ok", 1)),
testCase "Affected rows counting"
$ replicateM_ 13
$ let actualIO =
DSL.session $ do
dropTable
createTable
@ -453,17 +462,17 @@ tree =
deleteRows <* dropTable
where
dropTable =
DSL.statement () $
Statements.plain $
"drop table if exists a"
DSL.statement ()
$ Statements.plain
$ "drop table if exists a"
createTable =
DSL.statement () $
Statements.plain $
"create table a (id bigserial not null, name varchar not null, primary key (id))"
DSL.statement ()
$ Statements.plain
$ "create table a (id bigserial not null, name varchar not null, primary key (id))"
insertRow =
DSL.statement () $
Statements.plain $
"insert into a (name) values ('a')"
DSL.statement ()
$ Statements.plain
$ "insert into a (name) values ('a')"
deleteRows =
DSL.statement () $ Statement.Statement sql mempty decoder False
where
@ -472,18 +481,18 @@ tree =
decoder =
Decoders.rowsAffected
in actualIO >>= assertEqual "" (Right 100),
testCase "Result of an auto-incremented column" $
let actualIO =
DSL.session $ do
DSL.statement () $ Statements.plain $ "drop table if exists a"
DSL.statement () $ Statements.plain $ "create table a (id serial not null, v char not null, primary key (id))"
id1 <- DSL.statement () $ Statement.Statement "insert into a (v) values ('a') returning id" mempty (Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int4)) False
id2 <- DSL.statement () $ Statement.Statement "insert into a (v) values ('b') returning id" mempty (Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int4)) False
DSL.statement () $ Statements.plain $ "drop table if exists a"
pure (id1, id2)
in assertEqual "" (Right (1, 2)) =<< actualIO,
testCase "List decoding" $
let actualIO =
DSL.session $ DSL.statement () $ Statements.selectList
in assertEqual "" (Right [(1, 2), (3, 4), (5, 6)]) =<< actualIO
testCase "Result of an auto-incremented column"
$ let actualIO =
DSL.session $ do
DSL.statement () $ Statements.plain $ "drop table if exists a"
DSL.statement () $ Statements.plain $ "create table a (id serial not null, v char not null, primary key (id))"
id1 <- DSL.statement () $ Statement.Statement "insert into a (v) values ('a') returning id" mempty (Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int4)) False
id2 <- DSL.statement () $ Statement.Statement "insert into a (v) values ('b') returning id" mempty (Decoders.singleRow ((Decoders.column . Decoders.nonNullable) Decoders.int4)) False
DSL.statement () $ Statements.plain $ "drop table if exists a"
pure (id1, id2)
in assertEqual "" (Right (1, 2)) =<< actualIO,
testCase "List decoding"
$ let actualIO =
DSL.session $ DSL.statement () $ Statements.selectList
in assertEqual "" (Right [(1, 2), (3, 4), (5, 6)]) =<< actualIO
]

View File

@ -38,8 +38,8 @@ session session =
password = ""
database = "postgres"
use connection =
ExceptT $
fmap (mapLeft SessionError) $
Hasql.Session.run session connection
ExceptT
$ fmap (mapLeft SessionError)
$ Hasql.Session.run session connection
release connection =
lift $ HC.release connection

View File

@ -12,15 +12,18 @@ plain sql =
dropType :: ByteString -> HQ.Statement () ()
dropType name =
plain $
"drop type if exists " <> name
plain
$ "drop type if exists "
<> name
createEnum :: ByteString -> [ByteString] -> HQ.Statement () ()
createEnum name values =
plain $
"create type " <> name <> " as enum ("
<> mconcat (intersperse ", " (map (\x -> "'" <> x <> "'") values))
<> ")"
plain
$ "create type "
<> name
<> " as enum ("
<> mconcat (intersperse ", " (map (\x -> "'" <> x <> "'") values))
<> ")"
selectList :: HQ.Statement () ([] (Int64, Int64))
selectList =

View File

@ -15,9 +15,9 @@ main =
(,) <$> acquire <*> acquire
where
acquire =
join $
fmap (either (fail . show) return) $
Hasql.Connection.acquire connectionSettings
join
$ fmap (either (fail . show) return)
$ Hasql.Connection.acquire connectionSettings
where
connectionSettings =
Hasql.Connection.settings "localhost" 5432 "postgres" "" "postgres"