@@ -1,31 +1,36 @@
1−{-# LANGUAGE DataKinds #-}
2+ DataKinds
4+ , GeneralizedNewtypeDeriving
2−{-# LANGUAGE DeriveGeneric #-}
5+ , DeriveGeneric
3−{-# LANGUAGE DuplicateRecordFields #-}
6+ , DuplicateRecordFields
4−{-# LANGUAGE FlexibleContexts #-}
7+ , FlexibleContexts
5−{-# LANGUAGE OverloadedLabels #-}
8+ , OverloadedLabels
6−{-# LANGUAGE OverloadedLists #-}
9+ , OverloadedLists
7−{-# LANGUAGE OverloadedStrings #-}
10+ , OverloadedStrings
11+ , ScopedTypeVariables
8−{-# LANGUAGE TypeApplications #-}
12+ , TypeApplications
9−{-# LANGUAGE TypeFamilies #-}
13+ , TypeFamilies
10−{-# LANGUAGE TypeInType #-}
14+ , TypeInType
11−{-# LANGUAGE TypeOperators #-}
15+ , TypeOperators
12module ExceptionHandling
18module ExceptionHandling
14 , User (..)
20 , User (..)
18−import Control.Monad (void)
23+import Control.Monad(void)
19import Control.Monad.IO.Class (MonadIO (..))
24import Control.Monad.IO.Class (MonadIO (..))
20−import qualified Data.ByteString.Char8 as Char8
25+import qualified Data.ByteString.Char8 as Char8
21−import Data.Int (Int16)
26+import Data.Int (Int16)
22−import Data.Text (Text)
27+import Data.Text (Text)
23−import Data.Vector (Vector)
28+import Data.Vector (Vector)
24−import qualified Generics.SOP as SOP
29+import qualified Generics.SOP as SOP
25−import qualified GHC.Generics as GHC
30+import qualified GHC.Generics as GHC
26−import Squeal.PostgreSQL
31+import Squeal.PostgreSQL
27−import Squeal.PostgreSQL.Migration
32+import Squeal.PostgreSQL.Migration
28−import Test.Hspec
33+import Test.Hspec
30type Schema =
35type Schema =
31 '[ "users" ::: 'Table (
36 '[ "users" ::: 'Table (
@@ -52,16 +57,10 @@ data User =
52 User { userName :: Text
57 User { userName :: Text
53 , userEmail :: Maybe Text
58 , userEmail :: Maybe Text
54 , userVec :: VarArray (Vector (Maybe Int16)) }
59 , userVec :: VarArray (Vector (Maybe Int16)) }
55− deriving (Show, GHC.Generic)
60+ deriving (Show, GHC.Generic, Eq)
56instance SOP.Generic User
61instance SOP.Generic User
57instance SOP.HasDatatypeInfo User
62instance SOP.HasDatatypeInfo User
59−insertUser :: Manipulation '[] Schemas '[ 'NotNull 'PGtext, 'NotNull ('PGvararray ('Null 'PGint2))]
60− '[ "fromOnly" ::: 'NotNull 'PGint4 ]
61−insertUser = insertInto #users
62− (Values_ (Default `as` #id :* Set (param @1) `as` #name :* Set (param @2) `as` #vec))
63− OnConflictDoRaise (Returning (#id `as` #fromOnly))
65setup :: Definition (Public '[]) Schemas
64setup :: Definition (Public '[]) Schemas
67 createTable #users
66 createTable #users
@@ -98,16 +97,36 @@ dropDB = void . withConnection connectionString $
98 & pqThen (migrateDown (single migration))
97 & pqThen (migrateDown (single migration))
100connectionString :: Char8.ByteString
99connectionString :: Char8.ByteString
101−connectionString = "host=localhost port=5432 dbname=exampledb"
100+connectionString = "postgres:///exampledb"
103testUser :: User
102testUser :: User
104testUser = User "TestUser" Nothing (VarArray [])
103testUser = User "TestUser" Nothing (VarArray [])
106−newUser :: (MonadIO m, MonadPQ Schemas m) => User -> m ()
105+badTestUser :: User
107−newUser u = void $ manipulateParams insertUser (userName u, userVec u)
106+badTestUser = User "TestUser\NUL1" Nothing (VarArray [])
109−insertUserTwice :: (MonadIO m, MonadPQ Schemas m) => m ()
108+newUser :: (MonadIO m, MonadPQ Schemas m) => User -> m ()
110−insertUserTwice = newUser testUser >> newUser testUser
109+newUser u = void $ manipulateParams insertUser u
112+insertUser :: Manipulation_ Schemas User ()
113+insertUser = with (u `as` #u) e
115+ u = insertInto #users
116+ (Values_ (Default `as` #id :* Set (param @1) `as` #name :* Set (param @3) `as` #vec) )
117+ OnConflictDoRaise (Returning_ (#id :* param @2 `as` #email))
118+ e = insertInto_ #emails $ Select
119+ (Default `as` #id :* Set (#u ! #id) `as` #user_id :* Set (#u ! #email) `as` #email)
122+getUsers :: Query_ Schemas () User
124+ (#u ! #name `as` #userName :*
125+ #e ! #email `as` #userEmail :*
126+ #u ! #vec `as` #userVec )
127+ ( from (table (#users `as` #u)
128+ & innerJoin (table (#emails `as` #e ) )
129+ (#u ! #id .== #e ! #user_id )))
112specs :: SpecWith ()
131specs :: SpecWith ()
113specs = before_ setupDB $ after_ dropDB $
132specs = before_ setupDB $ after_ dropDB $
@@ -124,3 +143,9 @@ specs = before_ setupDB $ after_ dropDB $
124 it "should be rethrown for unique constraint violation in a manipulation by a transaction" $
143 it "should be rethrown for unique constraint violation in a manipulation by a transaction" $
125 withConnection connectionString (transactionally_ insertUserTwice)
144 withConnection connectionString (transactionally_ insertUserTwice)
126 `shouldThrow` (== dupKeyErr)
145 `shouldThrow` (== dupKeyErr)
147+ it "should be able to insert and then read a user" $ do
148+ fetchedUsers <- withConnection connectionString $ do
150+ getRows =<< runQuery getUsers
151+ fetchedUsers `shouldBe` [badTestUser]