hasql-1.10.3.3: src/library-tests/Sharing/ByUnit/Session/StatementSpec.hs
module Sharing.ByUnit.Session.StatementSpec (spec) where
import Data.Either
import Hasql.Connection qualified as Connection
import Hasql.Decoders qualified as Decoders
import Hasql.Encoders qualified as Encoders
import Hasql.Errors qualified as Errors
import Hasql.Session qualified as Session
import Hasql.Statement qualified as Statement
import Helpers.Scripts qualified as Scripts
import Test.Hspec
import Prelude
spec :: SpecWith (Text, Word16)
spec = do
describe "Roundtrips" do
it "handles simple values correctly" \config -> do
Scripts.onPreparableConnection config \connection -> do
result <- Connection.use connection (Session.statement (42 :: Int64) echoStatement)
result `shouldBe` Right 42
it "reuses a prepared statement across executions in one session" \config -> do
-- The first execution is a cache miss (separate PARSE roundtrip),
-- the second a cache hit (single roundtrip). Both must succeed.
Scripts.onPreparableConnection config \connection -> do
result <-
Connection.use connection do
a <- Session.statement (1 :: Int64) echoStatement
b <- Session.statement (2 :: Int64) echoStatement
pure (a, b)
result `shouldBe` Right (1, 2)
it "keeps a prepared statement usable after an EXECUTE error" \config -> do
-- Regression: PARSE succeeds, EXECUTE fails (division by zero). The
-- statement is on the server under its cached name, so a later use on the
-- same connection must hit the cache rather than re-issuing PARSE for an
-- already-existing name ("prepared statement ... already exists").
Scripts.onPreparableConnection config \connection -> do
failure <- Connection.use connection (Session.statement 0 divStatement)
failure `shouldSatisfy` isLeft
success <- Connection.use connection (Session.statement 1 divStatement)
success `shouldBe` Right 1
it "works on an unpreparable connection" \config -> do
Scripts.onUnpreparableConnection config \connection -> do
result <- Connection.use connection (Session.statement (42 :: Int64) echoStatement)
result `shouldBe` Right 42
describe "Error Handling" do
it "captures query errors correctly" \config -> do
Scripts.onPreparableConnection config \connection -> do
let statement =
Statement.preparable
"select true where 1 = any ($1) and $2"
( mconcat
[ fst >$< (Encoders.param (Encoders.nonNullable (Encoders.array (Encoders.dimension foldl' (Encoders.element (Encoders.nonNullable Encoders.int8)))))),
snd >$< (Encoders.param (Encoders.nonNullable Encoders.text))
]
)
(fmap (maybe False (const True)) (Decoders.rowMaybe (Decoders.column (Decoders.nonNullable Decoders.bool))))
result <- Connection.use connection (Session.statement ([3, 7] :: [Int64], "a") statement)
case result of
Left (Errors.StatementSessionError _ _ _ _ _ (Errors.ServerStatementError _)) -> pure ()
_ -> expectationFailure $ "Unexpected result: " <> show result
echoStatement :: Statement.Statement Int64 Int64
echoStatement =
Statement.preparable
"select $1"
(Encoders.param (Encoders.nonNullable Encoders.int8))
(Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.int8)))
-- | Parses fine, but fails at execution time when given 0 (division by zero).
divStatement :: Statement.Statement Int64 Int64
divStatement =
Statement.preparable
"select 1 / $1"
(Encoders.param (Encoders.nonNullable Encoders.int8))
(Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.int8)))