{-# LANGUAGE AllowAmbiguousTypes #-}
module Database.GP.Query
( WhereClauseExpr,
Field,
field,
whereClauseExprToSql,
whereClauseValues,
(&&.),
(||.),
(=.),
(>.),
(<.),
(>=.),
(<=.),
(<>.),
like,
between,
in',
isNull,
not',
params,
sqlFun,
allEntries,
idColumn,
byId,
byIdColumn,
orderBy,
SortOrder (..),
limit,
limitOffset,
NonEmpty(..)
)
where
import Data.Convertible (Convertible)
import Data.List (intercalate)
import Database.GP.Entity (Entity, columnNameFor, idField)
import Database.HDBC (SqlValue, toSql)
import qualified Data.List.NonEmpty as NE
import Data.List.NonEmpty (NonEmpty(..))
data CompareOp = Eq | Gt | Lt | GtEq | LtEq | NotEq | Like
data Field = Field [String] String
data WhereClauseExpr
= Where Field CompareOp SqlValue
| WhereBetween Field (SqlValue, SqlValue)
| WhereIn Field [SqlValue]
| WhereIsNull Field
| And WhereClauseExpr WhereClauseExpr
| Or WhereClauseExpr WhereClauseExpr
| Not WhereClauseExpr
| All
| ById SqlValue
| ByIdColumn
| OrderBy WhereClauseExpr (NonEmpty (Field, SortOrder))
| Limit WhereClauseExpr Int
| LimitOffset WhereClauseExpr Int Int
data SortOrder = ASC | DESC
deriving (Int -> SortOrder -> ShowS
[SortOrder] -> ShowS
SortOrder -> String
(Int -> SortOrder -> ShowS)
-> (SortOrder -> String)
-> ([SortOrder] -> ShowS)
-> Show SortOrder
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SortOrder -> ShowS
showsPrec :: Int -> SortOrder -> ShowS
$cshow :: SortOrder -> String
show :: SortOrder -> String
$cshowList :: [SortOrder] -> ShowS
showList :: [SortOrder] -> ShowS
Show)
field :: String -> Field
field :: String -> Field
field = [String] -> String -> Field
Field []
infixl 3 &&.
(&&.) :: WhereClauseExpr -> WhereClauseExpr -> WhereClauseExpr
&&. :: WhereClauseExpr -> WhereClauseExpr -> WhereClauseExpr
(&&.) = WhereClauseExpr -> WhereClauseExpr -> WhereClauseExpr
And
infixl 2 ||.
(||.) :: WhereClauseExpr -> WhereClauseExpr -> WhereClauseExpr
||. :: WhereClauseExpr -> WhereClauseExpr -> WhereClauseExpr
(||.) = WhereClauseExpr -> WhereClauseExpr -> WhereClauseExpr
Or
infixl 4 =., >., <., >=., <=., <>., `like`, `between`, `in'`
(=.), (>.), (<.), (>=.), (<=.), (<>.), like :: (Convertible b SqlValue) => Field -> b -> WhereClauseExpr
Field
a =. :: forall b. Convertible b SqlValue => Field -> b -> WhereClauseExpr
=. b
b = Field -> CompareOp -> SqlValue -> WhereClauseExpr
Where Field
a CompareOp
Eq (b -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql b
b)
Field
a >. :: forall b. Convertible b SqlValue => Field -> b -> WhereClauseExpr
>. b
b = Field -> CompareOp -> SqlValue -> WhereClauseExpr
Where Field
a CompareOp
Gt (b -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql b
b)
Field
a <. :: forall b. Convertible b SqlValue => Field -> b -> WhereClauseExpr
<. b
b = Field -> CompareOp -> SqlValue -> WhereClauseExpr
Where Field
a CompareOp
Lt (b -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql b
b)
Field
a >=. :: forall b. Convertible b SqlValue => Field -> b -> WhereClauseExpr
>=. b
b = Field -> CompareOp -> SqlValue -> WhereClauseExpr
Where Field
a CompareOp
GtEq (b -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql b
b)
Field
a <=. :: forall b. Convertible b SqlValue => Field -> b -> WhereClauseExpr
<=. b
b = Field -> CompareOp -> SqlValue -> WhereClauseExpr
Where Field
a CompareOp
LtEq (b -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql b
b)
Field
a <>. :: forall b. Convertible b SqlValue => Field -> b -> WhereClauseExpr
<>. b
b = Field -> CompareOp -> SqlValue -> WhereClauseExpr
Where Field
a CompareOp
NotEq (b -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql b
b)
Field
a like :: forall b. Convertible b SqlValue => Field -> b -> WhereClauseExpr
`like` b
b = Field -> CompareOp -> SqlValue -> WhereClauseExpr
Where Field
a CompareOp
Like (b -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql b
b)
between :: (Convertible a1 SqlValue, Convertible a2 SqlValue) => Field -> (a1, a2) -> WhereClauseExpr
Field
a between :: forall a1 a2.
(Convertible a1 SqlValue, Convertible a2 SqlValue) =>
Field -> (a1, a2) -> WhereClauseExpr
`between` (a1
b, a2
c) = Field -> (SqlValue, SqlValue) -> WhereClauseExpr
WhereBetween Field
a (a1 -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql a1
b, a2 -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql a2
c)
in' :: (Convertible b SqlValue) => Field -> [b] -> WhereClauseExpr
Field
a in' :: forall b. Convertible b SqlValue => Field -> [b] -> WhereClauseExpr
`in'` [b]
b = Field -> [SqlValue] -> WhereClauseExpr
WhereIn Field
a ((b -> SqlValue) -> [b] -> [SqlValue]
forall a b. (a -> b) -> [a] -> [b]
map b -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql [b]
b)
isNull :: Field -> WhereClauseExpr
isNull :: Field -> WhereClauseExpr
isNull = Field -> WhereClauseExpr
WhereIsNull
not' :: WhereClauseExpr -> WhereClauseExpr
not' :: WhereClauseExpr -> WhereClauseExpr
not' = WhereClauseExpr -> WhereClauseExpr
Not
allEntries :: WhereClauseExpr
allEntries :: WhereClauseExpr
allEntries = WhereClauseExpr
All
byId :: (Convertible a SqlValue) => a -> WhereClauseExpr
byId :: forall a. Convertible a SqlValue => a -> WhereClauseExpr
byId = SqlValue -> WhereClauseExpr
ById (SqlValue -> WhereClauseExpr)
-> (a -> SqlValue) -> a -> WhereClauseExpr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql
byIdColumn :: WhereClauseExpr
byIdColumn :: WhereClauseExpr
byIdColumn = WhereClauseExpr
ByIdColumn
sqlFun :: String -> Field -> Field
sqlFun :: String -> Field -> Field
sqlFun String
fun (Field [String]
funs String
name) = [String] -> String -> Field
Field (String
fun String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
funs) String
name
infixl 1 `orderBy`
orderBy :: WhereClauseExpr -> NonEmpty (Field, SortOrder) -> WhereClauseExpr
orderBy :: WhereClauseExpr -> NonEmpty (Field, SortOrder) -> WhereClauseExpr
orderBy = WhereClauseExpr -> NonEmpty (Field, SortOrder) -> WhereClauseExpr
OrderBy
limit :: WhereClauseExpr -> Int -> WhereClauseExpr
limit :: WhereClauseExpr -> Int -> WhereClauseExpr
limit = WhereClauseExpr -> Int -> WhereClauseExpr
Limit
limitOffset :: WhereClauseExpr -> (Int, Int) -> WhereClauseExpr
limitOffset :: WhereClauseExpr -> (Int, Int) -> WhereClauseExpr
limitOffset WhereClauseExpr
c (Int
offset, Int
lim) = WhereClauseExpr -> Int -> Int -> WhereClauseExpr
LimitOffset WhereClauseExpr
c Int
offset Int
lim
whereClauseExprToSql :: forall a. (Entity a) => WhereClauseExpr -> String
whereClauseExprToSql :: forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql (Where Field
f CompareOp
op SqlValue
_) = forall a. Entity a => Field -> String
columnToSql @a Field
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" " String -> ShowS
forall a. [a] -> [a] -> [a]
++ CompareOp -> String
opToSql CompareOp
op String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" ?"
whereClauseExprToSql (And WhereClauseExpr
e1 WhereClauseExpr
e2) = String
"(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
e1 String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
") AND (" String -> ShowS
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
e2 String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
whereClauseExprToSql (Or WhereClauseExpr
e1 WhereClauseExpr
e2) = String
"(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
e1 String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
") OR (" String -> ShowS
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
e2 String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
whereClauseExprToSql (Not (WhereIsNull Field
f)) = forall a. Entity a => Field -> String
columnToSql @a Field
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" IS NOT NULL"
whereClauseExprToSql (Not WhereClauseExpr
e) = String
"NOT (" String -> ShowS
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
e String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
whereClauseExprToSql (WhereBetween Field
f (SqlValue
_v1, SqlValue
_v2)) = forall a. Entity a => Field -> String
columnToSql @a Field
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" BETWEEN ? AND ?"
whereClauseExprToSql (WhereIn Field
f [SqlValue]
v) = forall a. Entity a => Field -> String
columnToSql @a Field
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" IN (" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
args String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
where
args :: String
args = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " (Int -> [String]
params ([SqlValue] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [SqlValue]
v))
whereClauseExprToSql (WhereIsNull Field
f) = forall a. Entity a => Field -> String
columnToSql @a Field
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" IS NULL"
whereClauseExprToSql WhereClauseExpr
All = String
"1=1"
whereClauseExprToSql (ById SqlValue
_eid) = forall a. Entity a => String
idColumn @a String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" = ?"
whereClauseExprToSql WhereClauseExpr
ByIdColumn = forall a. Entity a => String
idColumn @a String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" = ?"
whereClauseExprToSql (OrderBy WhereClauseExpr
clause NonEmpty (Field, SortOrder)
pairs) = forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
clause String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" ORDER BY " String -> ShowS
forall a. [a] -> [a] -> [a]
++ NonEmpty (Field, SortOrder) -> String
renderedPairs NonEmpty (Field, SortOrder)
pairs
where
renderedPairs :: NonEmpty (Field, SortOrder) -> String
renderedPairs :: NonEmpty (Field, SortOrder) -> String
renderedPairs NonEmpty (Field, SortOrder)
ne = String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " (NonEmpty String -> [String]
forall a. NonEmpty a -> [a]
NE.toList (((Field, SortOrder) -> String)
-> NonEmpty (Field, SortOrder) -> NonEmpty String
forall a b. (a -> b) -> NonEmpty a -> NonEmpty b
NE.map (\(Field
f,SortOrder
order) -> forall a. Entity a => Field -> String
columnToSql @a Field
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" " String -> ShowS
forall a. [a] -> [a] -> [a]
++ SortOrder -> String
forall a. Show a => a -> String
show SortOrder
order) NonEmpty (Field, SortOrder)
ne))
whereClauseExprToSql (Limit WhereClauseExpr
clause Int
x) = forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
clause String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" LIMIT " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
x
whereClauseExprToSql (LimitOffset WhereClauseExpr
clause Int
offset Int
lim) = forall a. Entity a => WhereClauseExpr -> String
whereClauseExprToSql @a WhereClauseExpr
clause String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" LIMIT " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
lim String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
" OFFSET " String -> ShowS
forall a. [a] -> [a] -> [a]
++ Int -> String
forall a. Show a => a -> String
show Int
offset
opToSql :: CompareOp -> String
opToSql :: CompareOp -> String
opToSql CompareOp
Eq = String
"="
opToSql CompareOp
Gt = String
">"
opToSql CompareOp
Lt = String
"<"
opToSql CompareOp
GtEq = String
">="
opToSql CompareOp
LtEq = String
"<="
opToSql CompareOp
NotEq = String
"<>"
opToSql CompareOp
Like = String
"LIKE"
columnToSql :: forall a. (Entity a) => Field -> String
columnToSql :: forall a. Entity a => Field -> String
columnToSql = forall a. Entity a => Field -> String
expandFunctions @a
idColumn :: forall a. (Entity a) => String
idColumn :: forall a. Entity a => String
idColumn = forall a. Entity a => ShowS
columnNameFor @a (forall a. Entity a => String
idField @a)
expandFunctions :: forall a. (Entity a) => Field -> String
expandFunctions :: forall a. Entity a => Field -> String
expandFunctions (Field [] String
name) = forall a. Entity a => ShowS
columnNameFor @a String
name
expandFunctions (Field (String
f : [String]
fs) String
name) = String
f String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"(" String -> ShowS
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => Field -> String
expandFunctions @a ([String] -> String -> Field
Field [String]
fs String
name) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
")"
whereClauseValues :: WhereClauseExpr -> [SqlValue]
whereClauseValues :: WhereClauseExpr -> [SqlValue]
whereClauseValues (Where Field
_ CompareOp
_ SqlValue
v) = [SqlValue -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql SqlValue
v]
whereClauseValues (And WhereClauseExpr
e1 WhereClauseExpr
e2) = WhereClauseExpr -> [SqlValue]
whereClauseValues WhereClauseExpr
e1 [SqlValue] -> [SqlValue] -> [SqlValue]
forall a. [a] -> [a] -> [a]
++ WhereClauseExpr -> [SqlValue]
whereClauseValues WhereClauseExpr
e2
whereClauseValues (Or WhereClauseExpr
e1 WhereClauseExpr
e2) = WhereClauseExpr -> [SqlValue]
whereClauseValues WhereClauseExpr
e1 [SqlValue] -> [SqlValue] -> [SqlValue]
forall a. [a] -> [a] -> [a]
++ WhereClauseExpr -> [SqlValue]
whereClauseValues WhereClauseExpr
e2
whereClauseValues (Not WhereClauseExpr
e) = WhereClauseExpr -> [SqlValue]
whereClauseValues WhereClauseExpr
e
whereClauseValues (WhereBetween Field
_ (SqlValue
v1, SqlValue
v2)) = [SqlValue -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql SqlValue
v1, SqlValue -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql SqlValue
v2]
whereClauseValues (WhereIn Field
_ [SqlValue]
v) = (SqlValue -> SqlValue) -> [SqlValue] -> [SqlValue]
forall a b. (a -> b) -> [a] -> [b]
map SqlValue -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql [SqlValue]
v
whereClauseValues (WhereIsNull Field
_) = []
whereClauseValues WhereClauseExpr
All = []
whereClauseValues (ById SqlValue
eid) = [SqlValue -> SqlValue
forall a. Convertible a SqlValue => a -> SqlValue
toSql SqlValue
eid]
whereClauseValues WhereClauseExpr
ByIdColumn = []
whereClauseValues (OrderBy WhereClauseExpr
clause NonEmpty (Field, SortOrder)
_) = WhereClauseExpr -> [SqlValue]
whereClauseValues WhereClauseExpr
clause
whereClauseValues (Limit WhereClauseExpr
clause Int
_) = WhereClauseExpr -> [SqlValue]
whereClauseValues WhereClauseExpr
clause
whereClauseValues (LimitOffset WhereClauseExpr
clause Int
_ Int
_) = WhereClauseExpr -> [SqlValue]
whereClauseValues WhereClauseExpr
clause
params :: Int -> [String]
params :: Int -> [String]
params Int
n = Int -> String -> [String]
forall a. Int -> a -> [a]
replicate Int
n String
"?"