Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a test utility for preparing statements without executing them #118

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions hasql.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ library
Hasql.Connection
Hasql.Statement
Hasql.Session
Hasql.TestUtils
other-modules:
Hasql.Private.Prelude
Hasql.Private.Errors
Expand All @@ -53,6 +54,7 @@ library
Hasql.Private.Encoders.Array
Hasql.Private.Encoders.Value
Hasql.Private.Encoders.Params
Hasql.Private.TestUtils
build-depends:
attoparsec >=0.10 && <0.14,
base >=4.9 && <5,
Expand Down
73 changes: 73 additions & 0 deletions library/Hasql/Private/TestUtils.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module Hasql.Private.TestUtils
where

import Hasql.Private.Prelude
import Hasql.Private.Connection
import Hasql.Private.Errors
import qualified Hasql.Private.Encoders as Encoders
import qualified Hasql.Private.Encoders.Params as Encoders.Params
import qualified Hasql.Private.IO as IO
import qualified Hasql.Statement as Statement
import qualified Hasql.Private.Connection as Connection

{-|
Prepare a 'Statement.Statement' to sanity check it against a schema.

To avoid requiring input parameter values, encoders are not used.
Instead, the types are determined by the query as described in the docs for
<https://hackage.haskell.org/package/postgresql-libpq-0.9.4.2/docs/Database-PostgreSQL-LibPQ.html#v:execParams postgresql-libpq>.

If using hasql-th, type casts in the query are already required, so the statements can be used as-is.

Note that this aims to catch many errors, but not all (e.g. field nullability is not checked).

E.g.,

Take the following statement (using hasql-th), which we assume matches our schema:

@
selectUserDetails :: Statement Int32 (Maybe (Text, Text, Maybe Text))
selectUserDetails =
[maybeStatement|
select name :: text, email :: text, phone :: text?
from user
where id = $1 :: int4
|]
@

>>> tryPrepare conn selectUserDetails
Nothing

Now, we change it by using the wrong type for the id field:

@
selectUserDetails' :: Statement Text (Maybe (Text, Text, Maybe Text))
selectUserDetails' =
[maybeStatement|
select name :: text, email :: text, phone :: text?
from user
where id = $1 :: text
|]
@
>>> tryPrepare conn selectUserDetails
Just (ResultError (ServerError "42883" "operator does not exist: integer = text" Nothing (Just "No operator matches the given name and argument types. You might need to add explicit type casts.")))

Or, we could change it to use a wrong column name:

@
selectUserDetails'' :: Statement Int32 (Maybe (Text, Text, Maybe Text))
selectUserDetails'' =
[maybeStatement|
select name :: text, email :: text, phone :: text?
from user
where ida = $1 :: int4
|]
@
>>> tryPrepare conn selectUserDetails
Just (ResultError (ServerError "42703" "column \"ida\" does not exist" Nothing (Just "Perhaps you meant to reference the column \"user.id\".")))
-}
tryPrepare :: Connection -> Statement.Statement a b -> IO (Maybe CommandError)
tryPrepare (Connection pqConnectionRef _ registry) (Statement.Statement template _ _ _) =
fmap (either Just (const Nothing))
$ withMVar pqConnectionRef
$ \pqConnection -> IO.getPreparedStatementKey pqConnection registry template []
7 changes: 7 additions & 0 deletions library/Hasql/TestUtils.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module Hasql.TestUtils
(
tryPrepare
)
where

import Hasql.Private.TestUtils