squeal-postgresql-0.1.0.0: test/Squeal/PostgreSQL/QuerySpec.hs
{-# LANGUAGE
DataKinds
, MagicHash
, OverloadedLabels
, OverloadedStrings
, TypeApplications
, TypeOperators
#-}
module Squeal.PostgreSQL.QuerySpec where
import Data.Function
import Generics.SOP hiding (from)
import Test.Hspec
import Squeal.PostgreSQL
spec :: Spec
spec = do
let
qry `queryRenders` str =
renderManipulation (queryStatement qry) `shouldBe` str
it "correctly renders a simple SELECT query" $ do
let
statement :: Query Tables '[] SumAndCol1
statement =
select ((#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
(from (Table (#table1 `As` #table1)) & where_ (#col1 .> #col2))
statement `queryRenders`
"SELECT (col1 + col2) AS sum, col1 AS col1\
\ FROM table1 AS table1 WHERE (col1 > col2);"
it "combines WHEREs using AND" $ do
let
statement :: Query Tables '[] SumAndCol1
statement =
select ((#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
(from (Table (#table1 `As` #table1)) & where_ true & where_ false)
statement `queryRenders`
"SELECT (col1 + col2) AS sum, col1 AS col1\
\ FROM table1 AS table1 WHERE (TRUE AND FALSE);"
it "performs sub SELECTs" $ do
let
statement :: Query Tables '[] SumAndCol1
statement =
selectStar (from (Subquery
(select ((#col1 + #col2) `As` #sum :* #col1 `As` #col1 :* Nil)
(from (Table (#table1 `As` #table1))) `As` #sub)))
statement `queryRenders`
"SELECT * FROM\
\ (SELECT (col1 + col2) AS sum, col1 AS col1\
\ FROM table1 AS table1) AS sub;"
it "does LIMIT clauses" $ do
let
statement :: Query Tables '[] Columns
statement = selectStar (from (Table (#table1 `As` #table1)) & limit 1)
statement `queryRenders` "SELECT * FROM table1 AS table1 LIMIT 1;"
it "should use the minimum of given LIMITs" $ do
let
statement :: Query Tables '[] Columns
statement =
selectStar (from (Table (#table1 `As` #table1)) & limit 1 & limit 2)
statement `queryRenders` "SELECT * FROM table1 AS table1 LIMIT 1;"
it "should render parameters using $ signs" $ do
let
statement :: Query Tables '[ 'Required ('NotNull 'PGbool)] Columns
statement = selectStar
(from (Table (#table1 `As` #table1)) & where_ (param @1))
statement `queryRenders`
"SELECT * FROM table1 AS table1 WHERE ($1 :: bool);"
it "does OFFSET clauses" $ do
let
statement :: Query Tables '[] Columns
statement =
selectStar (from (Table (#table1 `As` #table1)) & offset 1)
statement `queryRenders` "SELECT * FROM table1 AS table1 OFFSET 1;"
it "should use the sum of given OFFSETs" $ do
let
statement :: Query Tables '[] Columns
statement = selectStar
(from (Table (#table1 `As` #table1)) & offset 1 & offset 2)
statement `queryRenders` "SELECT * FROM table1 AS table1 OFFSET 3;"
it "should render GROUP BY and HAVING clauses" $ do
let
statement :: Query Tables '[] SumAndCol1
statement =
select (sum_ #col2 `As` #sum :* #col1 `As` #col1 :* Nil)
( from (Table (#table1 `As` #table1))
& group (By #col1 :* Nil)
& having (#col1 + sum_ #col2 .> 1) )
statement `queryRenders`
"SELECT sum(col2) AS sum, col1 AS col1\
\ FROM table1 AS table1\
\ GROUP BY col1\
\ HAVING ((col1 + sum(col2)) > 1);"
describe "JOINs" $ do
it "should render CROSS JOINs" $ do
let
statement :: Query JoinTables '[] ValueColumns
statement = select
( #orders ! #orderVal `As` #orderVal :*
#customers ! #customerVal `As` #customerVal :*
#shippers ! #shipperVal `As` #shipperVal :* Nil )
( from (Table (#orders `As` #orders)
& CrossJoin (Table (#customers `As` #customers))
& CrossJoin (Table (#shippers `As` #shippers))) )
statement `queryRenders`
"SELECT\
\ orders.orderVal AS orderVal,\
\ customers.customerVal AS customerVal,\
\ shippers.shipperVal AS shipperVal\
\ FROM orders AS orders\
\ CROSS JOIN customers AS customers\
\ CROSS JOIN shippers AS shippers;"
it "should render INNER JOINs" $ do
let
statement :: Query JoinTables '[] ValueColumns
statement = select
( (#orders ! #orderVal) `As` #orderVal
:* (#customers ! #customerVal) `As` #customerVal
:* (#shippers ! #shipperVal) `As` #shipperVal :* Nil)
( from (Table (#orders `As` #orders)
& InnerJoin (Table (#customers `As` #customers))
((#orders ! #customerID) .== (#customers ! #customerID))
& InnerJoin (Table (#shippers `As` #shippers))
((#orders ! #shipperID) .== (#shippers ! #shipperID))))
statement `queryRenders`
"SELECT\
\ orders.orderVal AS orderVal,\
\ customers.customerVal AS customerVal,\
\ shippers.shipperVal AS shipperVal\
\ FROM orders AS orders\
\ INNER JOIN customers AS customers\
\ ON (orders.customerID = customers.customerID)\
\ INNER JOIN shippers AS shippers\
\ ON (orders.shipperID = shippers.shipperID);"
it "should render self JOINs" $ do
let
statement :: Query JoinTables '[] OrderColumns
statement = selectDotStar #orders1
(from (Table (#orders `As` #orders1)
& CrossJoin (Table (#orders `As` #orders2))))
statement `queryRenders`
"SELECT orders1.*\
\ FROM orders AS orders1\
\ CROSS JOIN orders AS orders2;"
type Columns =
'[ "col1" ::: 'Required ('NotNull 'PGint4)
, "col2" ::: 'Required ('NotNull 'PGint4)
]
type Tables = '[ "table1" ::: Columns ]
type SumAndCol1 =
'[ "sum" ::: 'Required ('NotNull 'PGint4)
, "col1" ::: 'Required ('NotNull 'PGint4)
]
type StudentsColumns = '["name" ::: 'Required ('NotNull 'PGtext)]
type StudentsTable = '["students" ::: StudentsColumns]
type OrderColumns =
'[ "orderID" ::: 'Required ('NotNull 'PGint4)
, "orderVal" ::: 'Required ('NotNull 'PGtext)
, "customerID" ::: 'Required ('NotNull 'PGint4)
, "shipperID" ::: 'Required ('NotNull 'PGint4)
]
type CustomerColumns =
'[ "customerID" ::: 'Required ('NotNull 'PGint4)
, "customerVal" ::: 'Required ('NotNull 'PGfloat4)
]
type ShipperColumns =
'[ "shipperID" ::: 'Required ('NotNull 'PGint4)
, "shipperVal" ::: 'Required ('NotNull 'PGbool)
]
type JoinTables =
'[ "orders" ::: OrderColumns
, "customers" ::: CustomerColumns
, "shippers" ::: ShipperColumns
]
type ValueColumns =
'[ "orderVal" ::: 'Required ('NotNull 'PGtext)
, "customerVal" ::: 'Required ('NotNull 'PGfloat4)
, "shipperVal" ::: 'Required ('NotNull 'PGbool)
]