{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
-- {-# OPTIONS_GHC -ddump-splices #-}
module Main (main) where
import Data.Aeson (Value)
import Data.Int (Int32, Int64)
import Data.Scientific (Scientific)
import Data.Text (Text)
import Data.Time (Day, UTCTime)
import Data.UUID (UUID)
import Data.Word (Word64)
import GHC.Generics (Generic)
import Prelude
( Applicative(pure), Eq((==)), Maybe(Just, Nothing), MonadFail(fail)
, Semigroup((<>)), Show(show), ($), (.), Bool, IO, putStrLn
)
import Squeal.PostgreSQL
( NullType(NotNull, Null), Optionality(Def, NoDef)
, PGType(PGint4, PGjson, PGjsonb, PGtext, PGuuid), RenderSQL(renderSQL)
, SchemumType(Table), TableConstraint(ForeignKey, PrimaryKey), (:::), (:=>)
, Json, Jsonb, Only, Statement
)
import Squeal.QuasiQuotes (Field, ssql)
import Test.Hspec (describe, hspec, it)
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Generics.SOP as SOP
{- FOURMOLU_DISABLE -}
{- Copied mostly from the squeal documentation: -}
type UsersColumns =
'[ "id" ::: 'Def :=> 'NotNull 'PGtext
, "name" ::: 'NoDef :=> 'NotNull 'PGtext
, "employee_id" ::: 'NoDef :=> 'NotNull 'PGuuid
, "bio" ::: 'NoDef :=> 'Null 'PGtext
]
type UsersConstraints = '[ "pk_users" ::: 'PrimaryKey '["id"] ]
type EmailsColumns =
'[ "id" ::: 'Def :=> 'NotNull 'PGint4
, "user_id" ::: 'NoDef :=> 'NotNull 'PGtext
, "email" ::: 'NoDef :=> 'Null 'PGtext
]
type EmailsConstraints =
'[ "pk_emails" ::: 'PrimaryKey '["id"]
, "fk_user_id" ::: 'ForeignKey '["user_id"] "public" "users" '["id"]
]
type Schema =
'[ "users" ::: 'Table (UsersConstraints :=> UsersColumns)
, "emails" ::: 'Table (EmailsConstraints :=> EmailsColumns)
, "jsonb_test" ::: 'Table ('[] :=> '["data" ::: 'NoDef :=> 'NotNull 'PGjsonb])
, "json_test" ::: 'Table ('[] :=> '["data" ::: 'NoDef :=> 'NotNull 'PGjson])
, "users_copy" ::: 'Table (UsersCopyConstraints :=> UsersCopyColumns)
]
type UsersCopyColumns =
'[ "id" ::: 'NoDef :=> 'NotNull 'PGtext
, "name" ::: 'NoDef :=> 'NotNull 'PGtext
, "bio" ::: 'NoDef :=> 'Null 'PGtext
]
type UsersCopyConstraints = '[ "pk_users_copy" ::: 'PrimaryKey '["id"] ]
type DB =
'[ "public" ::: Schema
, "other" ::: Schema
]
{- FOURMOLU_ENABLE -}
data User = User
{ id :: Text
, name :: Text
}
deriving stock (Generic, Show)
deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
main :: IO ()
main =
hspec $ do
describe "queries" $ do
it "select * from users" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select * from public.users" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from public.users |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "SELECT * FROM \"users\" AS \"users\"" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| SELECT * FROM "users" AS "users" |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select * from users where name = 'bob'" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users where name = 'bob' |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" WHERE (\"name\" = (E'bob' :: text))"
checkStatement squealRendering statement
it "select * from users where id = $1" $ do
let
statement
:: Statement
DB
(Only Text)
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users where id = $1 |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" WHERE (\"id\" = ($1 :: text))"
checkStatement squealRendering statement
it "select users.name from users" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select users.name from users |]
squealRendering :: Text
squealRendering = "SELECT \"users\".\"name\" AS \"name\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select name from users" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select name from users |]
squealRendering :: Text
squealRendering = "SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select count(*) from users group by ()" $ do
let
statement :: Statement DB () (Field "_col1" Int64, ())
statement = [ssql| select count(*) from users group by () |]
squealRendering :: Text
squealRendering = "SELECT count(*) AS \"_col1\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select name, id from users" $ do
let
statement
:: Statement
DB
()
( Field "name" Text
, ( Field "id" Text
, ()
)
)
statement = [ssql| select name, id from users |]
squealRendering :: Text
squealRendering = "SELECT \"name\" AS \"name\", \"id\" AS \"id\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select id, name from users" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ()
)
)
statement = [ssql| select id, name from users |]
squealRendering :: Text
squealRendering = "SELECT \"id\" AS \"id\", \"name\" AS \"name\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select users.id, employee_id from users" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "employee_id" UUID
, ()
)
)
statement = [ssql| select users.id, employee_id from users |]
squealRendering :: Text
squealRendering =
"SELECT \"users\".\"id\" AS \"id\", \"employee_id\" AS \"employee_id\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select users.* from users" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select users.* from users |]
squealRendering :: Text
squealRendering = "SELECT \"users\".* FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select users.* from other.users" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select users.* from other.users |]
squealRendering :: Text
squealRendering = "SELECT \"users\".* FROM \"other\".\"users\" AS \"users\""
checkStatement squealRendering statement
it "select * from users limit 3" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users limit 3 |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" LIMIT 3"
checkStatement squealRendering statement
it "select * from users limit inline(lim)" $ do
let
mkStatement
:: Word64
-> Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
mkStatement lim =
[ssql| select * from users limit inline(lim) |]
squealRendering1 :: Text
squealRendering1 = "SELECT * FROM \"users\" AS \"users\" LIMIT 10"
squealRendering2 :: Text
squealRendering2 = "SELECT * FROM \"users\" AS \"users\" LIMIT 20"
checkStatement squealRendering1 (mkStatement 10)
checkStatement squealRendering2 (mkStatement 20)
it "select * from users offset inline(off)" $ do
let
mkStatement
:: Word64
-> Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
mkStatement off =
[ssql| select * from users offset inline(off) |]
squealRendering1 :: Text
squealRendering1 = "SELECT * FROM \"users\" AS \"users\" OFFSET 5"
squealRendering2 :: Text
squealRendering2 = "SELECT * FROM \"users\" AS \"users\" OFFSET 15"
checkStatement squealRendering1 (mkStatement 5)
checkStatement squealRendering2 (mkStatement 15)
it "select * from users offset 1" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users offset 1 |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" OFFSET 1"
checkStatement squealRendering statement
it "select users.id, employee_id as emp_id from users" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "emp_id" UUID
, ()
)
)
statement = [ssql| select users.id, employee_id as emp_id from users |]
squealRendering :: Text
squealRendering =
"SELECT \"users\".\"id\" AS \"id\", \"employee_id\" AS \"emp_id\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select users.id as user_id, employee_id from users" $ do
let
statement
:: Statement
DB
()
( Field "user_id" Text
, ( Field "employee_id" UUID
, ()
)
)
statement = [ssql| select users.id as user_id, employee_id from users |]
squealRendering :: Text
squealRendering =
"SELECT \"users\".\"id\" AS \"user_id\", \"employee_id\" AS \"employee_id\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it
"select users.id from users left outer join emails on emails.user_id = users.id"
$ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ()
)
statement =
[ssql|
select users.id
from users
left outer join emails
on emails.user_id = users.id
|]
squealRendering :: Text
squealRendering =
"SELECT \"users\".\"id\" AS \"id\" FROM \"users\" AS \"users\" LEFT OUTER JOIN \"emails\" AS \"emails\" ON (\"emails\".\"user_id\" = \"users\".\"id\")"
checkStatement squealRendering statement
it
"select users.id, users.name, emails.email from users left outer join emails on emails.user_id = users.id where emails.email = inline(\"targetEmail\")"
$ do
let
targetEmail :: Text
targetEmail = "foo@bar.com"
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "email" (Maybe Text)
, ()
)
)
)
statement =
[ssql|
select users.id, users.name, emails.email
from users
left outer join emails
on emails.user_id = users.id
where emails.email = inline("targetEmail")
|]
squealRendering :: Text
squealRendering =
"SELECT \"users\".\"id\" AS \"id\", \"users\".\"name\" AS \"name\", \"emails\".\"email\" AS \"email\" FROM \"users\" AS \"users\" LEFT OUTER JOIN \"emails\" AS \"emails\" ON (\"emails\".\"user_id\" = \"users\".\"id\") WHERE (\"emails\".\"email\" = (E'foo@bar.com' :: text))"
checkStatement squealRendering statement
it "select 'text_val'" $ do
let
statement
:: Statement
DB
()
( Field "_col1" Text
, ()
)
statement = [ssql| select 'text_val' |]
squealRendering :: Text
squealRendering = "SELECT * FROM (VALUES ((E'text_val' :: text))) AS t (\"_col1\")"
checkStatement squealRendering statement
it "select 1" $ do
let
statement
:: Statement
DB
()
( Field "_col1" Int64
, ()
)
statement = [ssql| select 1 |]
squealRendering :: Text
squealRendering = "SELECT * FROM (VALUES (1)) AS t (\"_col1\")"
checkStatement squealRendering statement
it "select 1 AS num, 'text_val' AS txt" $ do
let
statement
:: Statement
DB
()
( Field "num" Int64
, ( Field "txt" Text
, ()
)
)
statement = [ssql| select 1 AS num, 'text_val' AS txt |]
squealRendering :: Text
squealRendering = "SELECT * FROM (VALUES (1, (E'text_val' :: text))) AS t (\"num\", \"txt\")"
checkStatement squealRendering statement
it "values (1, 'a'), (2, 'b')" $ do
let
statement
:: Statement
DB
()
( Field "_column1" Int64
, ( Field "_column2" Text
, ()
)
)
statement = [ssql| values (1, 'a'), (2, 'b') |]
squealRendering :: Text
squealRendering =
"SELECT * FROM (VALUES (1, (E'a' :: text)), (2, (E'b' :: text))) AS t (\"_column1\", \"_column2\")"
checkStatement squealRendering statement
it "values (1, 'a')" $ do
let
statement
:: Statement
DB
()
( Field "_column1" Int64
, ( Field "_column2" Text
, ()
)
)
statement = [ssql| values (1, 'a') |]
squealRendering :: Text
squealRendering =
"SELECT * FROM (VALUES (1, (E'a' :: text))) AS t (\"_column1\", \"_column2\")"
checkStatement squealRendering statement
it "select * from (values (1, 'a'), (2, 'b')) as t (n, s)" $ do
let
statement
:: Statement
DB
()
( Field "n" Int64
, ( Field "s" Text
, ()
)
)
statement =
[ssql| select * from (values (1, 'a'), (2, 'b')) as t (n, s) |]
squealRendering :: Text
squealRendering =
"SELECT * FROM (SELECT * FROM (VALUES (1, (E'a' :: text)), (2, (E'b' :: text))) AS t (\"n\", \"s\")) AS \"t\""
checkStatement squealRendering statement
-- Selecting FROM a subquery (VALUES ...) alias is not yet supported by
-- the translator (SelectTableRef). This can be added in a follow-up.
-- Negative tests (compile-time failures) cannot be included in the
-- test suite because the quasiquoter fails during compilation.
-- Selecting FROM a subquery (VALUES ...) alias is not yet supported by
-- the translator (SelectTableRef). This can be added in a follow-up.
describe "group by" $ do
it "select name from users group by name" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select name from users group by name |]
squealRendering :: Text
squealRendering = "SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\" GROUP BY \"name\""
checkStatement squealRendering statement
it "select employee_id, count(id) from users group by employee_id" $ do
let
statement
:: Statement
DB
()
( Field "employee_id" UUID
, ( Field "_col2" Int64 -- Assuming count returns Int64
, ()
)
)
statement = [ssql| select employee_id, count(id) from users group by employee_id |]
squealRendering :: Text
squealRendering =
"SELECT \"employee_id\" AS \"employee_id\", count(ALL \"id\") AS \"_col2\" FROM \"users\" AS \"users\" GROUP BY \"employee_id\""
checkStatement squealRendering statement
it "select employee_id, name, count(id) from users group by employee_id, name" $ do
let
statement
:: Statement
DB
()
( Field "employee_id" UUID
, ( Field "name" Text
, ( Field "_col3" Int64
, ()
)
)
)
statement =
[ssql| select employee_id, name, count(id) from users group by employee_id, name |]
squealRendering :: Text
squealRendering =
"SELECT \"employee_id\" AS \"employee_id\", \"name\" AS \"name\", count(ALL \"id\") AS \"_col3\" FROM \"users\" AS \"users\" GROUP BY \"employee_id\", \"name\""
checkStatement squealRendering statement
describe "common table expressions" $ do
it "with users_cte as (select * from users) select * from users_cte" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement =
[ssql|
with users_cte as (select * from users)
select * from users_cte
|]
squealRendering :: Text
squealRendering =
"WITH \"users_cte\" AS (SELECT * FROM \"users\" AS \"users\") SELECT * FROM \"users_cte\" AS \"users_cte\""
checkStatement squealRendering statement
it
"with recursive t as ( select 1 as n union all select (n + 1) as n from t where n < 100) select n from t"
$ do
let
statement
:: Statement
DB
()
(Field "n" Int64, ())
statement =
[ssql|
with recursive t as (
select 1 as n
union all
select (n + 1) as n from t where n < 100
)
select n from t
|]
squealRendering :: Text
squealRendering =
"WITH RECURSIVE \"t\" AS ((SELECT * FROM (VALUES (1)) AS t (\"n\")) UNION ALL (SELECT (\"n\" + 1) AS \"n\" FROM \"t\" AS \"t\" WHERE (\"n\" < 100))) SELECT \"n\" AS \"n\" FROM \"t\" AS \"t\""
checkStatement squealRendering statement
it
"with recursive users_cte as ( select id, name from users union all select id, name from users_cte) select * from users_cte"
$ do
let
statement
:: Statement
DB
()
(Field "id" Text, (Field "name" Text, ()))
statement =
[ssql|
with recursive users_cte as (
select id, name from users
union all
select id, name from users_cte
)
select * from users_cte
|]
squealRendering :: Text
squealRendering =
"WITH RECURSIVE \"users_cte\" AS ((SELECT \"id\" AS \"id\", \"name\" AS \"name\" FROM \"users\" AS \"users\") UNION ALL (SELECT \"id\" AS \"id\", \"name\" AS \"name\" FROM \"users_cte\" AS \"users_cte\")) SELECT * FROM \"users_cte\" AS \"users_cte\""
checkStatement squealRendering statement
it
"with users_cte as (select * from users), emails_cte as (select * from emails) select users_cte.*, emails_cte.email from users_cte join emails_cte on users_cte.id = emails_cte.user_id"
$ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ( Field "email" (Maybe Text)
, ()
)
)
)
)
)
statement =
[ssql|
with
users_cte as (select * from users),
emails_cte as (select * from emails)
select users_cte.*, emails_cte.email
from users_cte
join emails_cte on users_cte.id = emails_cte.user_id
|]
squealRendering :: Text
squealRendering =
"WITH \"users_cte\" AS (SELECT * FROM \"users\" AS \"users\"), \"emails_cte\" AS (SELECT * FROM \"emails\" AS \"emails\") SELECT \"users_cte\".*, \"emails_cte\".\"email\" AS \"email\" FROM \"users_cte\" AS \"users_cte\" INNER JOIN \"emails_cte\" AS \"emails_cte\" ON (\"users_cte\".\"id\" = \"emails_cte\".\"user_id\")"
checkStatement squealRendering statement
describe "set operations" $ do
it "select name from users union select name from users_copy" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select name from users union select name from users_copy |]
squealRendering :: Text
squealRendering =
"(SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\") UNION (SELECT \"name\" AS \"name\" FROM \"users_copy\" AS \"users_copy\")"
checkStatement squealRendering statement
it "select name from users union all select name from users_copy" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select name from users union all select name from users_copy |]
squealRendering :: Text
squealRendering =
"(SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\") UNION ALL (SELECT \"name\" AS \"name\" FROM \"users_copy\" AS \"users_copy\")"
checkStatement squealRendering statement
it "select name from users intersect select name from users_copy" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select name from users intersect select name from users_copy |]
squealRendering :: Text
squealRendering =
"(SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\") INTERSECT (SELECT \"name\" AS \"name\" FROM \"users_copy\" AS \"users_copy\")"
checkStatement squealRendering statement
it "select name from users intersect all select name from users_copy" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select name from users intersect all select name from users_copy |]
squealRendering :: Text
squealRendering =
"(SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\") INTERSECT ALL (SELECT \"name\" AS \"name\" FROM \"users_copy\" AS \"users_copy\")"
checkStatement squealRendering statement
it "select name from users except select name from users_copy" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select name from users except select name from users_copy |]
squealRendering :: Text
squealRendering =
"(SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\") EXCEPT (SELECT \"name\" AS \"name\" FROM \"users_copy\" AS \"users_copy\")"
checkStatement squealRendering statement
it "select name from users except all select name from users_copy" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select name from users except all select name from users_copy |]
squealRendering :: Text
squealRendering =
"(SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\") EXCEPT ALL (SELECT \"name\" AS \"name\" FROM \"users_copy\" AS \"users_copy\")"
checkStatement squealRendering statement
it "(select name from users) union (select name from users_copy)" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| (select name from users) union (select name from users_copy) |]
squealRendering :: Text
squealRendering =
"(SELECT \"name\" AS \"name\" FROM \"users\" AS \"users\") UNION (SELECT \"name\" AS \"name\" FROM \"users_copy\" AS \"users_copy\")"
checkStatement squealRendering statement
describe "inserts" $ do
it "insert into emails (id, user_id, email) values (1, 'user-1', 'foo@bar')" $ do
let
statement
:: Statement DB () ()
statement =
[ssql|
insert into
emails (id, user_id, email)
values (1, 'user-1', 'foo@bar')
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (1, (E'user-1' :: text), (E'foo@bar' :: text))"
checkStatement squealRendering statement
it "insert into emails (id, user_id, email) values (1, 'user-1', $1)" $ do
let
statement
:: Statement
DB
(Only (Maybe Text))
()
statement =
[ssql|
insert into
emails (id, user_id, email)
values (1, 'user-1', $1)
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (1, (E'user-1' :: text), ($1 :: text))"
checkStatement squealRendering statement
it "insert into emails (id, user_id, email) values (1, $2, $1)" $ do
let
statement
:: Statement
DB
(Maybe Text, Text)
()
statement =
{-
Note the parameters are backwards (i.e. $2 comes before $1),
to test that you can do this kind of thing out of order.
-}
[ssql|
insert into
emails (id, user_id, email)
values (1, $2, $1)
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (1, ($2 :: text), ($1 :: text))"
checkStatement squealRendering statement
it "insert into users_copy (id, name, bio) values ($1, $2, $3)" $ do
let
statement
:: Statement
DB
(Text, Text, Maybe Text)
()
statement =
[ssql|
insert into
users_copy (id, name, bio)
values ($1, $2, $3)
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"users_copy\" AS \"users_copy\" (\"id\", \"name\", \"bio\") VALUES (($1 :: text), ($2 :: text), ($3 :: text))"
checkStatement squealRendering statement
it
"insert into emails (id, user_id, email) values (inline(i), inline(uid), inline_param(e))"
$ do
let
mkStatement :: Int32 -> Text -> Maybe Text -> Statement DB () ()
mkStatement i uid e =
[ssql|
insert into
emails (id, user_id, email)
values (inline(i), inline(uid), inline_param(e))
|]
checkStatement
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES ((1 :: int4), (E'user-1' :: text), NULL)"
(mkStatement 1 "user-1" Nothing)
checkStatement
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES ((1 :: int4), (E'user-1' :: text), (E'foo@bar.com' :: text))"
(mkStatement 1 "user-1" (Just "foo@bar.com"))
describe "default keyword" $ do
it "insert into emails (id, user_id, email) values (default, 'foo', 'bar')" $ do
let
statement
:: Statement
DB
(Maybe Text, Text)
()
statement =
[ssql|
insert into
emails (id, user_id, email)
values (default, 'foo', 'bar')
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (DEFAULT, (E'foo' :: text), (E'bar' :: text))"
checkStatement squealRendering statement
it "insert into emails (id, user_id, email) values (deFault, 'foo', 'bar')" $ do
let
statement
:: Statement
DB
(Maybe Text, Text)
()
statement =
[ssql|
insert into
emails (id, user_id, email)
values (deFault, 'foo', 'bar')
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (DEFAULT, (E'foo' :: text), (E'bar' :: text))"
checkStatement squealRendering statement
it "insert into emails (id, user_id, email) values (DEFAULT, 'foo', 'bar')" $ do
let
statement
:: Statement
DB
(Maybe Text, Text)
()
statement =
[ssql|
insert into
emails (id, user_id, email)
values (DEFAULT, 'foo', 'bar')
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (DEFAULT, (E'foo' :: text), (E'bar' :: text))"
checkStatement squealRendering statement
describe "null keyword" $ do
it "insert into emails (id, user_id, email) values (DEFAULT, 'foo', null)" $ do
let
statement
:: Statement
DB
(Maybe Text, Text)
()
statement =
[ssql|
insert into
emails (id, user_id, email)
values (DEFAULT, 'foo', null)
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (DEFAULT, (E'foo' :: text), NULL)"
checkStatement squealRendering statement
it "insert into emails (id, user_id, email) values (DEFAULT, 'foo', NULL)" $ do
let
statement
:: Statement
DB
(Maybe Text, Text)
()
statement =
[ssql|
insert into
emails (id, user_id, email)
values (DEFAULT, 'foo', NULL)
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (DEFAULT, (E'foo' :: text), NULL)"
checkStatement squealRendering statement
it "insert into emails (id, user_id, email) values (DEFAULT, 'foo', NuLL)" $ do
let
statement
:: Statement
DB
(Maybe Text, Text)
()
statement =
[ssql|
insert into
emails (id, user_id, email)
values (DEFAULT, 'foo', NuLL)
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (DEFAULT, (E'foo' :: text), NULL)"
checkStatement squealRendering statement
describe "insert ... select ..." $ do
it "insert into emails select id, user_id, email from emails where id = 1" $ do
let
statement :: Statement DB () ()
statement =
[ssql|
insert into emails
select id, user_id, email from emails where id = 1
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" SELECT \"id\" AS \"id\", \"user_id\" AS \"user_id\", \"email\" AS \"email\" FROM \"emails\" AS \"emails\" WHERE (\"id\" = 1)"
checkStatement squealRendering statement
it "insert into emails select id, user_id, email from emails where id = $1" $ do
let
statement :: Statement DB (Only Int32) ()
statement =
[ssql|
insert into emails
select id, user_id, email from emails where id = $1
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" SELECT \"id\" AS \"id\", \"user_id\" AS \"user_id\", \"email\" AS \"email\" FROM \"emails\" AS \"emails\" WHERE (\"id\" = ($1 :: int4))"
checkStatement squealRendering statement
it
"insert into users_copy select id, name, bio from users where users.id = 'uid1'"
$ do
let
statement :: Statement DB () ()
statement =
[ssql|
insert into users_copy
select id, name, bio from users where users.id = 'uid1'
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"users_copy\" AS \"users_copy\" SELECT \"id\" AS \"id\", \"name\" AS \"name\", \"bio\" AS \"bio\" FROM \"users\" AS \"users\" WHERE (\"users\".\"id\" = (E'uid1' :: text))"
checkStatement squealRendering statement
describe "returning clause" $ do
it
"insert into emails (id, user_id, email) values (1, 'user-1', 'foo@bar') returning id"
$ do
let
statement
:: Statement DB () (Field "id" Int32, ())
statement =
[ssql|
insert into
emails (id, user_id, email)
values (1, 'user-1', 'foo@bar')
returning id
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (1, (E'user-1' :: text), (E'foo@bar' :: text)) RETURNING \"id\" AS \"id\""
checkStatement squealRendering statement
it
"insert into emails (id, user_id, email) values (1, 'user-1', 'foo@bar') returning *"
$ do
let
statement
:: Statement
DB
()
( Field "id" Int32
, ( Field "user_id" Text
, ( Field "email" (Maybe Text)
, ()
)
)
)
statement =
[ssql|
insert into
emails (id, user_id, email)
values (1, 'user-1', 'foo@bar')
returning *
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"emails\" AS \"emails\" (\"id\", \"user_id\", \"email\") VALUES (1, (E'user-1' :: text), (E'foo@bar' :: text)) RETURNING *"
checkStatement squealRendering statement
describe "with common table expressions" $ do
it
"with new_user (id, name, bio) as (values ('id_new', 'new_name', 'new_bio')) insert into users_copy select * from new_user"
$ do
let
statement :: Statement DB () ()
statement =
[ssql|
with new_user (id, name, bio) as (values ('id_new', 'new_name', 'new_bio'))
insert into users_copy
select * from new_user
|]
squealRendering :: Text
squealRendering =
"WITH \"new_user\" AS (SELECT * FROM (VALUES ((E'id_new' :: text), (E'new_name' :: text), (E'new_bio' :: text))) AS t (\"id\", \"name\", \"bio\")) INSERT INTO \"users_copy\" AS \"users_copy\" SELECT * FROM \"new_user\" AS \"new_user\""
checkStatement squealRendering statement
describe "on conflict" $ do
it
"insert into users_copy (id, name, bio) values ('id1', 'name1', null) on conflict on constraint pk_users_copy do nothing"
$ do
let
statement :: Statement DB () ()
statement =
[ssql|
insert into users_copy (id, name, bio)
values ('id1', 'name1', null)
on conflict on constraint pk_users_copy do nothing
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"users_copy\" AS \"users_copy\" (\"id\", \"name\", \"bio\") VALUES ((E'id1' :: text), (E'name1' :: text), NULL) ON CONFLICT ON CONSTRAINT \"pk_users_copy\" DO NOTHING"
checkStatement squealRendering statement
it
"insert into users_copy (id, name, bio) values ('id1', 'name1', 'bio1') on conflict on constraint pk_users_copy do update set name = 'new_name'"
$ do
let
statement :: Statement DB () ()
statement =
[ssql|
insert into users_copy (id, name, bio)
values ('id1', 'name1', 'bio1')
on conflict on constraint pk_users_copy
do update set name = 'new_name'
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"users_copy\" AS \"users_copy\" (\"id\", \"name\", \"bio\") VALUES ((E'id1' :: text), (E'name1' :: text), (E'bio1' :: text)) ON CONFLICT ON CONSTRAINT \"pk_users_copy\" DO UPDATE SET \"name\" = (E'new_name' :: text)"
checkStatement squealRendering statement
it
"insert into users_copy (id, name, bio) values ('id1', 'name1', null) on conflict on constraint pk_users_copy do update set name = 'new_name' where users_copy.name = 'old_name'"
$ do
let
statement :: Statement DB () ()
statement =
[ssql|
insert into users_copy (id, name, bio)
values ('id1', 'name1', null)
on conflict on constraint pk_users_copy
do update set name = 'new_name'
where users_copy.name = 'old_name'
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"users_copy\" AS \"users_copy\" (\"id\", \"name\", \"bio\") VALUES ((E'id1' :: text), (E'name1' :: text), NULL) ON CONFLICT ON CONSTRAINT \"pk_users_copy\" DO UPDATE SET \"name\" = (E'new_name' :: text) WHERE (\"users_copy\".\"name\" = (E'old_name' :: text))"
checkStatement squealRendering statement
it
"insert into users_copy (id, name, bio) values ('id1', 'name1', null) on conflict on constraint pk_users_copy do nothing returning id"
$ do
let
statement :: Statement DB () (Field "id" Text, ())
statement =
[ssql|
insert into users_copy (id, name, bio)
values ('id1', 'name1', null)
on conflict on constraint pk_users_copy do nothing
returning id
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"users_copy\" AS \"users_copy\" (\"id\", \"name\", \"bio\") VALUES ((E'id1' :: text), (E'name1' :: text), NULL) ON CONFLICT ON CONSTRAINT \"pk_users_copy\" DO NOTHING RETURNING \"id\" AS \"id\""
checkStatement squealRendering statement
it
"insert into users_copy (id, name, bio) values ('id1', 'name1', 'bio1') on conflict on constraint pk_users_copy do update set name = 'new_name' returning *"
$ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "bio" (Maybe Text)
, ()
)
)
)
statement =
[ssql|
insert into users_copy (id, name, bio)
values ('id1', 'name1', 'bio1')
on conflict on constraint pk_users_copy
do update set name = 'new_name'
returning *
|]
squealRendering :: Text
squealRendering =
"INSERT INTO \"users_copy\" AS \"users_copy\" (\"id\", \"name\", \"bio\") VALUES ((E'id1' :: text), (E'name1' :: text), (E'bio1' :: text)) ON CONFLICT ON CONSTRAINT \"pk_users_copy\" DO UPDATE SET \"name\" = (E'new_name' :: text) RETURNING *"
checkStatement squealRendering statement
describe "deletes" $ do
it "delete from users where true" $ do
let
statement :: Statement DB () ()
statement = [ssql| delete from users where true |]
squealRendering :: Text
squealRendering = "DELETE FROM \"users\" AS \"users\" WHERE TRUE"
checkStatement squealRendering statement
it "delete from emails where id = 1" $ do
let
statement :: Statement DB () ()
statement = [ssql| delete from emails where id = 1 |]
squealRendering :: Text
squealRendering =
"DELETE FROM \"emails\" AS \"emails\" WHERE (\"id\" = 1)"
checkStatement squealRendering statement
it "delete from emails where id = $1" $ do
let
statement :: Statement DB (Only Int32) ()
statement = [ssql| delete from emails where id = $1 |]
squealRendering :: Text
squealRendering = "DELETE FROM \"emails\" AS \"emails\" WHERE (\"id\" = ($1 :: int4))"
checkStatement squealRendering statement
it "delete from emails where email = inline(e)" $ do
let
statement :: Statement DB () ()
statement = [ssql| delete from emails where email = inline(e) |]
e :: Text
e = "foo"
squealRendering :: Text
squealRendering =
"DELETE FROM \"emails\" AS \"emails\" WHERE (\"email\" = (E'foo' :: text))"
checkStatement squealRendering statement
it "delete from users where id = 'some-id' returning id" $ do
let
statement :: Statement DB () (Field "id" Text, ())
statement = [ssql| delete from users where id = 'some-id' returning id |]
squealRendering :: Text
squealRendering =
"DELETE FROM \"users\" AS \"users\" WHERE (\"id\" = (E'some-id' :: text)) RETURNING \"id\" AS \"id\""
checkStatement squealRendering statement
describe "with common table expressions" $ do
it
"with to_delete as (select id from users where name = 'Alice') delete from users where id in (select to_delete.id from to_delete)"
$ do
let
statement :: Statement DB () ()
statement =
[ssql|
with to_delete as (select id from users where name = 'Alice')
delete from users
where id in (select to_delete.id from to_delete)
|]
squealRendering :: Text
squealRendering =
"WITH \"to_delete\" AS (SELECT \"id\" AS \"id\" FROM \"users\" AS \"users\" WHERE (\"name\" = (E'Alice' :: text))) DELETE FROM \"users\" AS \"users\" WHERE (\"id\" = ANY (SELECT \"to_delete\".\"id\" AS \"id\" FROM \"to_delete\" AS \"to_delete\"))"
checkStatement squealRendering statement
it
"with to_delete as (select id from users where name = 'Alice') delete from users using to_delete where users.id = to_delete.id"
$ do
let
statement :: Statement DB () ()
statement =
[ssql|
with to_delete as (select id from users where name = 'Alice')
delete from users
using to_delete
where users.id = to_delete.id
|]
squealRendering :: Text
squealRendering =
"WITH \"to_delete\" AS (SELECT \"id\" AS \"id\" FROM \"users\" AS \"users\" WHERE (\"name\" = (E'Alice' :: text))) DELETE FROM \"users\" AS \"users\" USING \"to_delete\" AS \"to_delete\" WHERE (\"users\".\"id\" = \"to_delete\".\"id\")"
checkStatement squealRendering statement
describe "updates" $ do
it "update users set name = 'new name' where id = 'some-id'" $ do
let
statement :: Statement DB () ()
statement = [ssql| update users set name = 'new name' where id = 'some-id' |]
squealRendering :: Text
squealRendering =
"UPDATE \"users\" AS \"users\" SET \"name\" = (E'new name' :: text) WHERE (\"id\" = (E'some-id' :: text))"
checkStatement squealRendering statement
it "update users set name = 'new name', bio = 'new bio' where id = 'some-id'" $ do
let
statement :: Statement DB () ()
statement =
[ssql| update users set name = 'new name', bio = 'new bio' where id = 'some-id' |]
squealRendering :: Text
squealRendering =
"UPDATE \"users\" AS \"users\" SET \"name\" = (E'new name' :: text), \"bio\" = (E'new bio' :: text) WHERE (\"id\" = (E'some-id' :: text))"
checkStatement squealRendering statement
it "update users set name = inline(n) where id = 'some-id'" $ do
let
n :: Text
n = "new name"
statement :: Statement DB () ()
statement = [ssql| update users set name = inline(n) where id = 'some-id' |]
squealRendering :: Text
squealRendering =
"UPDATE \"users\" AS \"users\" SET \"name\" = (E'new name' :: text) WHERE (\"id\" = (E'some-id' :: text))"
checkStatement squealRendering statement
it "update users set name = $1 where id = $2" $ do
let
statement :: Statement DB (Text, Text) ()
statement = [ssql| update users set name = $1 where id = $2 |]
squealRendering :: Text
squealRendering =
"UPDATE \"users\" AS \"users\" SET \"name\" = ($1 :: text) WHERE (\"id\" = ($2 :: text))"
checkStatement squealRendering statement
it "update users set name = 'new name' where id = 'some-id' returning id" $ do
let
statement :: Statement DB () (Field "id" Text, ())
statement =
[ssql| update users set name = 'new name' where id = 'some-id' returning id |]
squealRendering :: Text
squealRendering =
"UPDATE \"users\" AS \"users\" SET \"name\" = (E'new name' :: text) WHERE (\"id\" = (E'some-id' :: text)) RETURNING \"id\" AS \"id\""
checkStatement squealRendering statement
describe "with common table expressions" $ do
it
"with to_update as (select id from users where name = 'Alice') update users set name = 'Alicia' from to_update where users.id = to_update.id"
$ do
let
statement :: Statement DB () ()
statement =
[ssql|
with to_update as (select id from users where name = 'Alice')
update users
set name = 'Alicia'
from to_update
where users.id = to_update.id
|]
squealRendering :: Text
squealRendering =
"WITH \"to_update\" AS (SELECT \"id\" AS \"id\" FROM \"users\" AS \"users\" WHERE (\"name\" = (E'Alice' :: text))) UPDATE \"users\" AS \"users\" SET \"name\" = (E'Alicia' :: text) FROM \"to_update\" AS \"to_update\" WHERE (\"users\".\"id\" = \"to_update\".\"id\")"
checkStatement squealRendering statement
describe "scalar expressions" $ do
-- Binary Operators
it "select users.id != 'no-such-user' as neq from users" $ do
let
stmt :: Statement DB () (Field "neq" (Maybe Bool), ())
stmt = [ssql| select users.id != 'no-such-user' as neq from users |]
squealRendering :: Text
squealRendering =
"SELECT (\"users\".\"id\" <> (E'no-such-user' :: text)) AS "
<> "\"neq\" FROM \"users\" AS \"users\""
checkStatement squealRendering stmt
it "select * from users where users.id <> 'no-such-user'" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
stmt = [ssql| select * from users where users.id <> 'no-such-user' |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE (\"users\".\"id\" <> (E'no-such-user' :: text))"
checkStatement squealRendering stmt
it "select * from emails where emails.id > 0" $ do
let
stmt
:: Statement
DB
()
(Field "id" Int32, (Field "user_id" Text, (Field "email" (Maybe Text), ())))
stmt = [ssql| select * from emails where emails.id > 0 |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"emails\" AS \"emails\" WHERE (\"emails\".\"id\" > 0)"
checkStatement squealRendering stmt
it "select * from emails where emails.id >= 0" $ do
let
stmt
:: Statement
DB
()
(Field "id" Int32, (Field "user_id" Text, (Field "email" (Maybe Text), ())))
stmt = [ssql| select * from emails where emails.id >= 0 |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"emails\" AS \"emails\" WHERE (\"emails\".\"id\" >= 0)"
checkStatement squealRendering stmt
it "select * from emails where emails.id < 10" $ do
let
stmt
:: Statement
DB
()
(Field "id" Int32, (Field "user_id" Text, (Field "email" (Maybe Text), ())))
stmt = [ssql| select * from emails where emails.id < 10 |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"emails\" AS \"emails\" WHERE (\"emails\".\"id\" < 10)"
checkStatement squealRendering stmt
it "select * from emails where emails.id <= 10" $ do
let
stmt
:: Statement
DB
()
(Field "id" Int32, (Field "user_id" Text, (Field "email" (Maybe Text), ())))
stmt = [ssql| select * from emails where emails.id <= 10 |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"emails\" AS \"emails\" WHERE (\"emails\".\"id\" <= 10)"
checkStatement squealRendering stmt
it "select emails.id + 1 as plus_one from emails" $ do
let
stmt :: Statement DB () (Field "plus_one" Int32, ())
stmt = [ssql| select emails.id + 1 as plus_one from emails |]
squealRendering :: Text
squealRendering =
"SELECT (\"emails\".\"id\" + 1) AS \"plus_one\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select emails.id - 1 as minus_one from emails" $ do
let
stmt :: Statement DB () (Field "minus_one" Int32, ())
stmt = [ssql| select emails.id - 1 as minus_one from emails |]
squealRendering :: Text
squealRendering =
"SELECT (\"emails\".\"id\" - 1) AS \"minus_one\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select emails.id * 2 as times_two from emails" $ do
let
stmt :: Statement DB () (Field "times_two" Int32, ())
stmt = [ssql| select emails.id * 2 as times_two from emails |]
squealRendering :: Text
squealRendering =
"SELECT (\"emails\".\"id\" * 2) AS \"times_two\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select * from users where users.id = 'a' and users.name = 'b'" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where users.id = 'a' and users.name = 'b' |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE ((\"users\".\"id\" = (E'a' :: text)) AND (\"users\".\"name\" = (E'b' :: text)))"
checkStatement squealRendering stmt
it "select * from users where users.id = 'a' or users.name = 'b'" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where users.id = 'a' or users.name = 'b' |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE ((\"users\".\"id\" = (E'a' :: text)) OR (\"users\".\"name\" = (E'b' :: text)))"
checkStatement squealRendering stmt
it "select * from users where users.name like 'A%'" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where users.name like 'A%' |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE (\"users\".\"name\" LIKE (E'A%' :: text))"
checkStatement squealRendering stmt
it "select * from users where users.name ilike 'a%'" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where users.name ilike 'a%' |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE (\"users\".\"name\" ILIKE (E'a%' :: text))"
checkStatement squealRendering stmt
-- Prefix Operators
it "select * from users where not (users.name = 'no-one')" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where not (users.name = 'no-one') |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE (NOT (\"users\".\"name\" = (E'no-one' :: text)))"
checkStatement squealRendering stmt
it "select -emails.id as neg_id from emails" $ do
let
stmt :: Statement DB () (Field "neg_id" Int32, ())
stmt = [ssql| select -emails.id as neg_id from emails |]
squealRendering :: Text
squealRendering =
"SELECT ((0 :: int4) - \"emails\".\"id\") AS \"neg_id\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
-- Postfix Operators
it "select * from users where users.bio is null" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where users.bio is null |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" WHERE \"users\".\"bio\" IS NULL"
checkStatement squealRendering stmt
it "select * from users where users.bio is not null" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where users.bio is not null |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" WHERE \"users\".\"bio\" IS NOT NULL"
checkStatement squealRendering stmt
describe "function calls" $ do
-- Function Calls
it "select coalesce(users.bio, 'no bio') as bio from users" $ do
let
stmt :: Statement DB () (Field "bio" Text, ())
stmt = [ssql| select coalesce(users.bio, 'no bio') as bio from users |]
squealRendering :: Text
squealRendering =
"SELECT COALESCE(\"users\".\"bio\", (E'no bio' :: text)) AS \"bio\" FROM \"users\" AS \"users\""
checkStatement squealRendering stmt
it "select lower(users.name) as lower_name from users" $ do
let
stmt :: Statement DB () (Field "lower_name" Text, ())
stmt = [ssql| select lower(users.name) as lower_name from users |]
squealRendering :: Text
squealRendering =
"SELECT lower(\"users\".\"name\") AS \"lower_name\" FROM \"users\" AS \"users\""
checkStatement squealRendering stmt
it "select char_length(users.name) as name_len from users" $ do
let
stmt :: Statement DB () (Field "name_len" Int32, ())
stmt = [ssql| select char_length(users.name) as name_len from users |]
squealRendering :: Text
squealRendering =
"SELECT char_length(\"users\".\"name\") AS \"name_len\" FROM \"users\" AS \"users\""
checkStatement squealRendering stmt
it "select character_length(users.name) as name_len_alias from users" $ do
let
stmt :: Statement DB () (Field "name_len_alias" Int32, ())
stmt = [ssql| select character_length(users.name) as name_len_alias from users |]
squealRendering :: Text
squealRendering =
"SELECT char_length(\"users\".\"name\") AS \"name_len_alias\" FROM \"users\" AS \"users\""
checkStatement squealRendering stmt
it "select \"upper\"(users.name) as upper_name from users" $ do
let
stmt :: Statement DB () (Field "upper_name" Text, ())
stmt = [ssql| select "upper"(users.name) as upper_name from users |]
squealRendering :: Text
squealRendering =
"SELECT upper(\"users\".\"name\") AS \"upper_name\" FROM \"users\" AS \"users\""
checkStatement squealRendering stmt
it "select now() as current_time" $ do
let
stmt :: Statement DB () (Field "current_time" UTCTime, ())
stmt = [ssql| select now() as current_time |]
squealRendering :: Text
squealRendering = "SELECT * FROM (VALUES (now())) AS t (\"current_time\")"
checkStatement squealRendering stmt
it "select current_date as today" $ do
let
stmt :: Statement DB () (Field "today" Day, ())
stmt = [ssql| select current_date as today |]
squealRendering :: Text
squealRendering = "SELECT * FROM (VALUES (CURRENT_DATE)) AS t (\"today\")"
checkStatement squealRendering stmt
describe "aggregate functions" $ do
it "select sum(id) as total_ids from emails" $ do
let
stmt :: Statement DB () (Field "total_ids" (Maybe Int64), ())
stmt = [ssql| select sum(id) as total_ids from emails group by () |]
squealRendering :: Text
squealRendering =
"SELECT sum(ALL \"id\") AS \"total_ids\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select sum(all id) as total_ids from emails" $ do
let
stmt :: Statement DB () (Field "total_ids" (Maybe Int64), ())
stmt = [ssql| select sum(all id) as total_ids from emails group by () |]
squealRendering :: Text
squealRendering =
"SELECT sum(ALL \"id\") AS \"total_ids\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select sum(distinct id) as total_ids from emails" $ do
let
stmt :: Statement DB () (Field "total_ids" (Maybe Int64), ())
stmt = [ssql| select sum(distinct id) as total_ids from emails group by () |]
squealRendering :: Text
squealRendering =
"SELECT sum(DISTINCT \"id\") AS \"total_ids\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select count(distinct id) as distinct_ids from emails" $ do
let
stmt :: Statement DB () (Field "distinct_ids" Int64, ())
stmt = [ssql| select count(distinct id) as distinct_ids from emails group by () |]
squealRendering :: Text
squealRendering =
"SELECT count(DISTINCT \"id\") AS \"distinct_ids\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select count(all id) as all_ids from emails" $ do
let
stmt :: Statement DB () (Field "all_ids" Int64, ())
stmt = [ssql| select count(all id) as all_ids from emails group by () |]
squealRendering :: Text
squealRendering =
"SELECT count(ALL \"id\") AS \"all_ids\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select avg(id) as avg_id from emails" $ do
let
stmt
:: Statement
DB
()
( Field "avg_id" (Maybe Scientific)
, ()
)
stmt = [ssql| select avg(id) as avg_id from emails group by () |]
squealRendering :: Text
squealRendering =
"SELECT avg(ALL \"id\") AS \"avg_id\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select min(id) as min_id from emails" $ do
let
stmt :: Statement DB () (Field "min_id" (Maybe Int32), ())
stmt = [ssql| select min(id) as min_id from emails group by () |]
squealRendering :: Text
squealRendering =
"SELECT min(ALL \"id\") AS \"min_id\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "select max(id) as max_id from emails" $ do
let
stmt :: Statement DB () (Field "max_id" (Maybe Int32), ())
stmt = [ssql| select max(id) as max_id from emails group by () |]
squealRendering :: Text
squealRendering =
"SELECT max(ALL \"id\") AS \"max_id\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
it "haskell variables in expressions" $ do
let
mkStatement
:: Text
-> Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
mkStatement haskellVariable =
[ssql| select * from users where name = inline("haskellVariable") |]
squealRendering1 :: Text
squealRendering1 = "SELECT * FROM \"users\" AS \"users\" WHERE (\"name\" = (E'Alice' :: text))"
squealRendering2 :: Text
squealRendering2 = "SELECT * FROM \"users\" AS \"users\" WHERE (\"name\" = (E'Bob' :: text))"
checkStatement squealRendering1 (mkStatement "Alice")
checkStatement squealRendering2 (mkStatement "Bob")
-- PARENS (implicitly tested by complex expressions)
it "select (emails.id + 1) * 2 as calc from emails" $ do
let
stmt :: Statement DB () (Field "calc" Int32, ())
stmt = [ssql| select (emails.id + 1) * 2 as calc from emails |]
squealRendering :: Text
squealRendering =
"SELECT ((\"emails\".\"id\" + 1) * 2) AS \"calc\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering stmt
-- IN / NOT IN
it "select * from users where users.name in ('Alice', 'Bob')" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where users.name in ('Alice', 'Bob') |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE \"users\".\"name\" IN ((E'Alice' :: text), (E'Bob' :: text))"
checkStatement squealRendering stmt
it "select * from users where users.id in (select emails.user_id from emails)" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt =
[ssql| select * from users where users.id in (select emails.user_id from emails) |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE (\"users\".\"id\" = ANY (SELECT \"emails\".\"user_id\" AS \"user_id\" FROM \"emails\" AS \"emails\"))"
checkStatement squealRendering stmt
it "select * from users where users.name not in ('Alice', 'Bob')" $ do
let
stmt
:: Statement
DB
()
( Field "id" Text
, (Field "name" Text, (Field "employee_id" UUID, (Field "bio" (Maybe Text), ())))
)
stmt = [ssql| select * from users where users.name not in ('Alice', 'Bob') |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"users\" AS \"users\" WHERE \"users\".\"name\" NOT IN ((E'Alice' :: text), (E'Bob' :: text))"
checkStatement squealRendering stmt
-- BETWEEN / NOT BETWEEN
it "select * from emails where emails.id between 0 and 10" $ do
let
stmt
:: Statement
DB
()
(Field "id" Int32, (Field "user_id" Text, (Field "email" (Maybe Text), ())))
stmt = [ssql| select * from emails where emails.id between 0 and 10 |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"emails\" AS \"emails\" WHERE \"emails\".\"id\" BETWEEN 0 AND 10"
checkStatement squealRendering stmt
it "select * from emails where emails.id not between 0 and 10" $ do
let
stmt
:: Statement
DB
()
(Field "id" Int32, (Field "user_id" Text, (Field "email" (Maybe Text), ())))
stmt = [ssql| select * from emails where emails.id not between 0 and 10 |]
squealRendering :: Text
squealRendering =
"SELECT * FROM \"emails\" AS \"emails\" WHERE \"emails\".\"id\" NOT BETWEEN 0 AND 10"
checkStatement squealRendering stmt
-- CAST
it "select (e.id :: text) as casted_id from emails as e" $ do
let
stmt :: Statement DB () (Field "casted_id" Text, ())
stmt = [ssql| select (e.id :: text) as casted_id from emails as e |]
squealRendering :: Text
squealRendering = "SELECT (\"e\".\"id\" :: text) AS \"casted_id\" FROM \"emails\" AS \"e\""
checkStatement squealRendering stmt
it "select * from users for update" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users for update |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" FOR UPDATE"
checkStatement squealRendering statement
it "select * from users for read only" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users for read only |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select * from users where name = 'bob' for read only" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users where name = 'bob' for read only |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" WHERE (\"name\" = (E'bob' :: text))"
checkStatement squealRendering statement
it "with users_cte as (select * from users) select * from users_cte for read only" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement =
[ssql|
with users_cte as (select * from users)
select * from users_cte for read only
|]
squealRendering :: Text
squealRendering =
"WITH \"users_cte\" AS (SELECT * FROM \"users\" AS \"users\") SELECT * FROM \"users_cte\" AS \"users_cte\""
checkStatement squealRendering statement
it "select * from jsonb_test" $ do
let
statement
:: Statement
DB
()
( Field "data" (Jsonb Value)
, ()
)
statement = [ssql| select * from jsonb_test |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"jsonb_test\" AS \"jsonb_test\""
checkStatement squealRendering statement
it "select * from json_test" $ do
let
statement
:: Statement
DB
()
( Field "data" (Json Value)
, ()
)
statement = [ssql| select * from json_test |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"json_test\" AS \"json_test\""
checkStatement squealRendering statement
it "select distinct name from users" $ do
let
statement :: Statement DB () (Field "name" Text, ())
statement = [ssql| select distinct name from users |]
squealRendering :: Text
squealRendering = "SELECT DISTINCT \"name\" AS \"name\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select distinct * from users" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select distinct * from users |]
squealRendering :: Text
squealRendering = "SELECT DISTINCT * FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select distinct on (employee_id) employee_id, name from users" $ do
let
statement
:: Statement
DB
()
( Field "employee_id" UUID
, ( Field "name" Text
, ()
)
)
statement = [ssql| select distinct on (employee_id) employee_id, name from users |]
squealRendering :: Text
squealRendering =
"SELECT DISTINCT ON (\"employee_id\") \"employee_id\" AS \"employee_id\", \"name\" AS \"name\" FROM \"users\" AS \"users\" ORDER BY \"employee_id\" ASC"
checkStatement squealRendering statement
it "select distinct on (employee_id, name) employee_id, name, id from users" $ do
let
statement
:: Statement
DB
()
( Field "employee_id" UUID
, ( Field "name" Text
, ( Field "id" Text
, ()
)
)
)
statement =
[ssql| select distinct on (employee_id, name) employee_id, name, id from users |]
squealRendering :: Text
squealRendering =
"SELECT DISTINCT ON (\"employee_id\", \"name\") \"employee_id\" AS \"employee_id\", \"name\" AS \"name\", \"id\" AS \"id\" FROM \"users\" AS \"users\" ORDER BY \"employee_id\" ASC, \"name\" ASC"
checkStatement squealRendering statement
describe "order by" $ do
it "select * from users order by name" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users order by name |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" ORDER BY \"name\" ASC"
checkStatement squealRendering statement
it "select * from users order by name asc" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users order by name asc |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" ORDER BY \"name\" ASC"
checkStatement squealRendering statement
it "select * from users order by name desc" $ do
let
statement
:: Statement
DB
()
( Field "id" Text
, ( Field "name" Text
, ( Field "employee_id" UUID
, ( Field "bio" (Maybe Text)
, ()
)
)
)
)
statement = [ssql| select * from users order by name desc |]
squealRendering :: Text
squealRendering = "SELECT * FROM \"users\" AS \"users\" ORDER BY \"name\" DESC"
checkStatement squealRendering statement
describe "having clause" $ do
it
"select employee_id, count(id) from users group by employee_id having count(id) > 1"
$ do
let
statement
:: Statement
DB
()
( Field "employee_id" UUID
, ( Field "_col2" Int64 -- Assuming count returns Int64
, ()
)
)
statement =
[ssql|
select employee_id, count(id)
from users
group by employee_id
having count(id) > 1
|]
squealRendering :: Text
squealRendering =
"SELECT \"employee_id\" AS \"employee_id\", count(ALL \"id\") AS \"_col2\" FROM \"users\" AS \"users\" GROUP BY \"employee_id\" HAVING (count(ALL \"id\") > 1)"
checkStatement squealRendering statement
describe "window functions" $ do
it "select name, row_number() over () as rn from users" $ do
let
statement
:: Statement
DB
()
( Field "name" Text
, ( Field "rn" Int64
, ()
)
)
statement = [ssql| select name, row_number() over () as rn from users |]
squealRendering :: Text
squealRendering =
"SELECT \"name\" AS \"name\", row_number() OVER () AS \"rn\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it
"select name, rank() over (partition by employee_id order by name) as r from users"
$ do
let
statement
:: Statement
DB
()
( Field "name" Text
, ( Field "r" Int64
, ()
)
)
statement =
[ssql| select name, rank() over (partition by employee_id order by name) as r from users |]
squealRendering :: Text
squealRendering =
"SELECT \"name\" AS \"name\", rank() OVER (PARTITION BY \"employee_id\" ORDER BY \"name\" ASC) AS \"r\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it "select email, sum(id) over (partition by user_id) as user_total from emails" $ do
let
statement
:: Statement
DB
()
( Field "email" (Maybe Text)
, ( Field "user_total" (Maybe Int64)
, ()
)
)
statement =
[ssql| select email, sum(id) over (partition by user_id) as user_total from emails |]
squealRendering :: Text
squealRendering =
"SELECT \"email\" AS \"email\", sum(\"id\") OVER (PARTITION BY \"user_id\") AS \"user_total\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering statement
it "select email, avg(id) over (partition by user_id) as user_avg from emails" $ do
let
statement
:: Statement
DB
()
( Field "email" (Maybe Text)
, ( Field "user_avg" (Maybe Scientific)
, ()
)
)
statement =
[ssql| select email, avg(id) over (partition by user_id) as user_avg from emails |]
squealRendering :: Text
squealRendering =
"SELECT \"email\" AS \"email\", avg(\"id\") OVER (PARTITION BY \"user_id\") AS \"user_avg\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering statement
it "select email, min(id) over (partition by user_id) as user_min from emails" $ do
let
statement
:: Statement
DB
()
( Field "email" (Maybe Text)
, ( Field "user_min" (Maybe Int32)
, ()
)
)
statement =
[ssql| select email, min(id) over (partition by user_id) as user_min from emails |]
squealRendering :: Text
squealRendering =
"SELECT \"email\" AS \"email\", min(\"id\") OVER (PARTITION BY \"user_id\") AS \"user_min\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering statement
it "select email, max(id) over (partition by user_id) as user_max from emails" $ do
let
statement
:: Statement
DB
()
( Field "email" (Maybe Text)
, ( Field "user_max" (Maybe Int32)
, ()
)
)
statement =
[ssql| select email, max(id) over (partition by user_id) as user_max from emails |]
squealRendering :: Text
squealRendering =
"SELECT \"email\" AS \"email\", max(\"id\") OVER (PARTITION BY \"user_id\") AS \"user_max\" FROM \"emails\" AS \"emails\""
checkStatement squealRendering statement
it
"select name, row_number() over (order by name), rank() over (order by name) from users"
$ do
let
statement
:: Statement
DB
()
( Field "name" Text
, ( Field "_window1" Int64
, ( Field "_window2" Int64
, ()
)
)
)
statement =
[ssql| select name, row_number() over (order by name), rank() over (order by name) from users |]
squealRendering :: Text
squealRendering =
"SELECT \"name\" AS \"name\", row_number() OVER ( ORDER BY \"name\" ASC) AS \"_window1\", rank() OVER ( ORDER BY \"name\" ASC) AS \"_window2\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
it
"select name, row_number() over (partition by employee_id order by name), rank() over (order by name) from users"
$ do
let
statement
:: Statement
DB
()
( Field "name" Text
, ( Field "_window1" Int64
, ( Field "_window2" Int64
, ()
)
)
)
statement =
[ssql| select name, row_number() over (partition by employee_id order by name), rank() over (order by name) from users |]
squealRendering :: Text
squealRendering =
"SELECT \"name\" AS \"name\", row_number() OVER (PARTITION BY \"employee_id\" ORDER BY \"name\" ASC) AS \"_window1\", rank() OVER ( ORDER BY \"name\" ASC) AS \"_window2\" FROM \"users\" AS \"users\""
checkStatement squealRendering statement
_printQuery :: (RenderSQL a) => a -> IO ()
_printQuery = putStrLn . T.unpack . TE.decodeUtf8 . renderSQL
checkStatement
:: (RenderSQL sql)
=> Text
-> sql
-> IO ()
checkStatement expect statement =
let
rendered :: Text
rendered = TE.decodeUtf8 (renderSQL statement)
in
if rendered == expect
then
pure ()
else
fail $
"SQL statements do not match.\nExpected:\n"
<> show expect
<> "\nActual:\n"
<> show rendered
<> "\n"