{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE LambdaCase #-}
module Database.GP.SqlGenerator
( insertStmtFor,
insertReturningStmtFor,
updateStmtFor,
selectFromStmt,
deleteStmtFor,
createTableStmtFor,
dropTableStmtFor,
columnTypeFor,
WhereClauseExpr,
Field,
field,
whereClauseValues,
(&&.),
(||.),
(=.),
(>.),
(<.),
(>=.),
(<=.),
(<>.),
like,
between,
in',
isNull,
not',
sqlFun,
allEntries,
byId,
byIdColumn,
orderBy,
SortOrder (..),
limit,
limitOffset,
NonEmpty(..),
Database (..),
)
where
import Data.List (intercalate)
import Database.GP.Entity
import Database.GP.Query
insertStmtFor :: forall a. Entity a => String
insertStmtFor :: forall a. Entity a => String
insertStmtFor =
String
"INSERT INTO "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
tableName @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" ("
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
columns
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
") VALUES ("
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " (Int -> [String]
params ([String] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
columns))
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
");"
where
columns :: [String]
columns = forall a. Entity a => [String]
columnNamesFor @a
insertReturningStmtFor :: forall a. Entity a => String
insertReturningStmtFor :: forall a. Entity a => String
insertReturningStmtFor =
String
"INSERT INTO "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
tableName @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" ("
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
insertColumns
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
") VALUES ("
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " (Int -> [String]
params ([String] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [String]
insertColumns))
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
") RETURNING "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
returnColumns
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";"
where
columns :: [String]
columns = forall a. Entity a => [String]
columnNamesFor @a
insertColumns :: [String]
insertColumns = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= forall a. Entity a => String
idColumn @a) [String]
columns
returnColumns :: String
returnColumns = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
columns
columnNamesFor :: forall a. Entity a => [String]
columnNamesFor :: forall a. Entity a => [String]
columnNamesFor = ((String, String) -> String) -> [(String, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, String) -> String
forall a b. (a, b) -> b
snd [(String, String)]
fieldColumnPairs
where
fieldColumnPairs :: [(String, String)]
fieldColumnPairs = forall a. Entity a => [(String, String)]
fieldsToColumns @a
updateStmtFor :: forall a. (Entity a) => String
updateStmtFor :: forall a. Entity a => String
updateStmtFor =
String
"UPDATE "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
tableName @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" SET "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
updatePairs
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" WHERE "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
idColumn @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" = ?"
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";"
where
updatePairs :: [String]
updatePairs = (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" = ?") (forall a. Entity a => [String]
columnNamesFor @a)
selectFromStmt :: forall a. (Entity a) => WhereClauseExpr -> String
selectFromStmt :: forall a. Entity a => WhereClauseExpr -> String
selectFromStmt WhereClauseExpr
whereClauseExpr =
String
"SELECT "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " (forall a. Entity a => [String]
columnNamesFor @a)
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" FROM "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
tableName @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" WHERE "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
whereClauseExpr
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";"
deleteStmtFor :: forall a. (Entity a) => String
deleteStmtFor :: forall a. Entity a => String
deleteStmtFor =
String
"DELETE FROM "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
tableName @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" WHERE "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
idColumn @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" = ?;"
data Database = Postgres | SQLite
deriving (Int -> Database -> String -> String
[Database] -> String -> String
Database -> String
(Int -> Database -> String -> String)
-> (Database -> String)
-> ([Database] -> String -> String)
-> Show Database
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> Database -> String -> String
showsPrec :: Int -> Database -> String -> String
$cshow :: Database -> String
show :: Database -> String
$cshowList :: [Database] -> String -> String
showList :: [Database] -> String -> String
Show, Database -> Database -> Bool
(Database -> Database -> Bool)
-> (Database -> Database -> Bool) -> Eq Database
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Database -> Database -> Bool
== :: Database -> Database -> Bool
$c/= :: Database -> Database -> Bool
/= :: Database -> Database -> Bool
Eq)
createTableStmtFor :: forall a. (Entity a) => Database -> String
createTableStmtFor :: forall a. Entity a => Database -> String
createTableStmtFor Database
dbServer =
String
"CREATE TABLE "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
tableName @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" ("
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " (((String, String) -> String) -> [(String, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
f, String
c) -> String
c String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" " String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => Database -> String -> String
columnTypeFor @a Database
dbServer String
f String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
optionalPK String
f) (forall a. Entity a => [(String, String)]
fieldsToColumns @a))
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
");"
where
isIdField :: String -> Bool
isIdField String
f = String
f String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== forall a. Entity a => String
idField @a
optionalPK :: String -> String
optionalPK String
f = if String -> Bool
isIdField String
f then String
" PRIMARY KEY" else String
""
columnTypeFor :: forall a. (Entity a) => Database -> String -> String
columnTypeFor :: forall a. Entity a => Database -> String -> String
columnTypeFor Database
dbDialect String
fieldName =
case Database
dbDialect of
Database
SQLite -> String -> String
columnTypeForSQLite String
fType
Database
Postgres -> String -> String
columnTypeForPostgres String
fType
where
maybeFType :: Maybe TypeRep
maybeFType = forall a. Entity a => String -> Maybe TypeRep
maybeFieldTypeFor @a String
fieldName
fType :: String
fType = String -> (TypeRep -> String) -> Maybe TypeRep -> String
forall b a. b -> (a -> b) -> Maybe a -> b
maybe String
"OTHER" TypeRep -> String
forall a. Show a => a -> String
show Maybe TypeRep
maybeFType
columnTypeForSQLite :: String -> String
columnTypeForSQLite :: String -> String
columnTypeForSQLite = \case
String
"Int" -> String
"INTEGER"
String
"[Char]" -> String
"TEXT"
String
"Double" -> String
"REAL"
String
"Float" -> String
"REAL"
String
"Bool" -> String
"INT"
String
_ -> String
"TEXT"
columnTypeForPostgres :: String -> String
columnTypeForPostgres :: String -> String
columnTypeForPostgres = \case
String
"Int" -> String
"numeric"
String
"[Char]" -> String
"varchar"
String
"Double" -> String
"numeric"
String
"Float" -> String
"numeric"
String
"Bool" -> String
"boolean"
String
_ -> String
"varchar"
dropTableStmtFor :: forall a. (Entity a) => String
dropTableStmtFor :: forall a. Entity a => String
dropTableStmtFor =
String
"DROP TABLE IF EXISTS "
String -> String -> String
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
tableName @a
String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";"