{-# LANGUAGE DataKinds , DeriveGeneric , FlexibleContexts , OverloadedLabels , OverloadedStrings , OverloadedLists , TypeApplications , TypeOperators #-} module Main (main, main2) where import Control.Monad (void) import Control.Monad.Base (liftBase, MonadBase) import Data.Int (Int16, Int32) import Data.Monoid ((<>)) import Data.Text (Text) import Data.Vector (Vector) import Squeal.PostgreSQL import qualified Data.ByteString.Char8 as Char8 import qualified Generics.SOP as SOP import qualified GHC.Generics as GHC type Schema = '[ "users" ::: 'Table ( '[ "pk_users" ::: 'PrimaryKey '["id"] ] :=> '[ "id" ::: 'Def :=> 'NotNull 'PGint4 , "name" ::: 'NoDef :=> 'NotNull 'PGtext , "vec" ::: 'NoDef :=> 'NotNull ('PGvararray 'PGint2) ]) , "emails" ::: 'Table ( '[ "pk_emails" ::: 'PrimaryKey '["id"] , "fk_user_id" ::: 'ForeignKey '["user_id"] "users" '["id"] ] :=> '[ "id" ::: 'Def :=> 'NotNull 'PGint4 , "user_id" ::: 'NoDef :=> 'NotNull 'PGint4 , "email" ::: 'NoDef :=> 'Null 'PGtext ]) ] setup :: Definition '[] Schema setup = createTable #users ( serial `As` #id :* (text & notNullable) `As` #name :* (vararray int2 & notNullable) `As` #vec :* Nil ) ( primaryKey #id `As` #pk_users :* Nil ) >>> createTable #emails ( serial `As` #id :* (int & notNullable) `As` #user_id :* (text & nullable) `As` #email :* Nil ) ( primaryKey #id `As` #pk_emails :* foreignKey #user_id #users #id OnDeleteCascade OnUpdateCascade `As` #fk_user_id :* Nil ) teardown :: Definition Schema '[] teardown = dropTable #emails >>> dropTable #users insertUser :: Manipulation Schema '[ 'NotNull 'PGtext, 'NotNull ('PGvararray 'PGint2)] '[ "fromOnly" ::: 'NotNull 'PGint4 ] insertUser = insertRows #users (Default `As` #id :* Set (param @1) `As` #name :* Set (param @2) `As` #vec :* Nil) [] OnConflictDoNothing (Returning (#id `As` #fromOnly :* Nil)) insertEmail :: Manipulation Schema '[ 'NotNull 'PGint4, 'Null 'PGtext] '[] insertEmail = insertRows #emails ( Default `As` #id :* Set (param @1) `As` #user_id :* Set (param @2) `As` #email :* Nil ) [] OnConflictDoNothing (Returning Nil) getUsers :: Query Schema '[] '[ "userName" ::: 'NotNull 'PGtext , "userEmail" ::: 'Null 'PGtext , "userVec" ::: 'NotNull ('PGvararray 'PGint2)] getUsers = select (#u ! #name `As` #userName :* #e ! #email `As` #userEmail :* #u ! #vec `As` #userVec :* Nil) ( from (table (#users `As` #u) & innerJoin (table (#emails `As` #e)) (#u ! #id .== #e ! #user_id)) ) data User = User { userName :: Text, userEmail :: Maybe Text, userVec :: Vector (Maybe Int16) } deriving (Show, GHC.Generic) instance SOP.Generic User instance SOP.HasDatatypeInfo User users :: [User] users = [ User "Alice" (Just "alice@gmail.com") [Nothing, Just 1] , User "Bob" Nothing [Just 2, Nothing] , User "Carole" (Just "carole@hotmail.com") [Just 3] ] session :: (MonadBase IO pq, MonadPQ Schema pq) => pq () session = do liftBase $ Char8.putStrLn "manipulating" idResults <- traversePrepared insertUser ([(userName user, userVec user) | user <- users]) ids <- traverse (fmap fromOnly . getRow 0) idResults traversePrepared_ insertEmail (zip (ids :: [Int32]) (userEmail <$> users)) liftBase $ Char8.putStrLn "querying" usersResult <- runQuery getUsers usersRows <- getRows usersResult liftBase $ print (usersRows :: [User]) main :: IO () main = do Char8.putStrLn "squeal" connectionString <- pure "host=localhost port=5432 dbname=exampledb" Char8.putStrLn $ "connecting to " <> connectionString connection0 <- connectdb connectionString Char8.putStrLn "setting up schema" connection1 <- execPQ (define setup) connection0 connection2 <- execPQ session connection1 Char8.putStrLn "tearing down schema" connection3 <- execPQ (define teardown) connection2 finish connection3 main2 :: IO () main2 = void . withConnection "host=localhost port=5432 dbname=exampledb" $ define setup & pqThen session & pqThen (define teardown)