{-# LANGUAGE BangPatterns, TypeFamilies, DataKinds, PolyKinds, TypeApplications, ScopedTypeVariables, ConstraintKinds, TypeOperators, GADTs, UndecidableInstances, StandaloneDeriving, FunctionalDependencies, FlexibleContexts, InstanceSigs, AllowAmbiguousTypes, DeriveAnyClass #-}
module IHP.QueryBuilder.Types
(
QueryBuilder (..)
, SQLQuery (..)
, Condition (..)
, ConditionValue (..)
, OrderByClause (..)
, OrderByDirection (..)
, FilterOperator (..)
, MatchSensitivity (..)
, addCondition
, qualifyAndJoinColumns
, DefaultScope (..)
, EqOrIsOperator (..)
, FilterPrimaryKey (..)
) where
import IHP.Prelude
import IHP.ModelSupport
import IHP.HSX.Markup (ToHtml(..))
import qualified Control.DeepSeq as DeepSeq
import qualified GHC.Generics
import qualified Hasql.Encoders as Encoders
import qualified Prelude
import qualified Data.Text as Text
data MatchSensitivity = CaseSensitive | CaseInsensitive deriving (Int -> MatchSensitivity -> ShowS
[MatchSensitivity] -> ShowS
MatchSensitivity -> String
(Int -> MatchSensitivity -> ShowS)
-> (MatchSensitivity -> String)
-> ([MatchSensitivity] -> ShowS)
-> Show MatchSensitivity
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> MatchSensitivity -> ShowS
showsPrec :: Int -> MatchSensitivity -> ShowS
$cshow :: MatchSensitivity -> String
show :: MatchSensitivity -> String
$cshowList :: [MatchSensitivity] -> ShowS
showList :: [MatchSensitivity] -> ShowS
Show, MatchSensitivity -> MatchSensitivity -> Bool
(MatchSensitivity -> MatchSensitivity -> Bool)
-> (MatchSensitivity -> MatchSensitivity -> Bool)
-> Eq MatchSensitivity
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: MatchSensitivity -> MatchSensitivity -> Bool
== :: MatchSensitivity -> MatchSensitivity -> Bool
$c/= :: MatchSensitivity -> MatchSensitivity -> Bool
/= :: MatchSensitivity -> MatchSensitivity -> Bool
Eq)
data FilterOperator
= EqOp
| NotEqOp
| InOp
| NotInOp
| IsOp
| IsNotOp
| LikeOp !MatchSensitivity
| NotLikeOp !MatchSensitivity
| MatchesOp !MatchSensitivity
| GreaterThanOp
| GreaterThanOrEqualToOp
| LessThanOp
| LessThanOrEqualToOp
| SqlOp
deriving (Int -> FilterOperator -> ShowS
[FilterOperator] -> ShowS
FilterOperator -> String
(Int -> FilterOperator -> ShowS)
-> (FilterOperator -> String)
-> ([FilterOperator] -> ShowS)
-> Show FilterOperator
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> FilterOperator -> ShowS
showsPrec :: Int -> FilterOperator -> ShowS
$cshow :: FilterOperator -> String
show :: FilterOperator -> String
$cshowList :: [FilterOperator] -> ShowS
showList :: [FilterOperator] -> ShowS
Show, FilterOperator -> FilterOperator -> Bool
(FilterOperator -> FilterOperator -> Bool)
-> (FilterOperator -> FilterOperator -> Bool) -> Eq FilterOperator
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: FilterOperator -> FilterOperator -> Bool
== :: FilterOperator -> FilterOperator -> Bool
$c/= :: FilterOperator -> FilterOperator -> Bool
/= :: FilterOperator -> FilterOperator -> Bool
Eq)
data OrderByClause =
OrderByClause
{ OrderByClause -> Text
orderByColumn :: !Text
, OrderByClause -> OrderByDirection
orderByDirection :: !OrderByDirection }
deriving (Int -> OrderByClause -> ShowS
[OrderByClause] -> ShowS
OrderByClause -> String
(Int -> OrderByClause -> ShowS)
-> (OrderByClause -> String)
-> ([OrderByClause] -> ShowS)
-> Show OrderByClause
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> OrderByClause -> ShowS
showsPrec :: Int -> OrderByClause -> ShowS
$cshow :: OrderByClause -> String
show :: OrderByClause -> String
$cshowList :: [OrderByClause] -> ShowS
showList :: [OrderByClause] -> ShowS
Show, OrderByClause -> OrderByClause -> Bool
(OrderByClause -> OrderByClause -> Bool)
-> (OrderByClause -> OrderByClause -> Bool) -> Eq OrderByClause
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OrderByClause -> OrderByClause -> Bool
== :: OrderByClause -> OrderByClause -> Bool
$c/= :: OrderByClause -> OrderByClause -> Bool
/= :: OrderByClause -> OrderByClause -> Bool
Eq, (forall x. OrderByClause -> Rep OrderByClause x)
-> (forall x. Rep OrderByClause x -> OrderByClause)
-> Generic OrderByClause
forall x. Rep OrderByClause x -> OrderByClause
forall x. OrderByClause -> Rep OrderByClause x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. OrderByClause -> Rep OrderByClause x
from :: forall x. OrderByClause -> Rep OrderByClause x
$cto :: forall x. Rep OrderByClause x -> OrderByClause
to :: forall x. Rep OrderByClause x -> OrderByClause
GHC.Generics.Generic, OrderByClause -> ()
(OrderByClause -> ()) -> NFData OrderByClause
forall a. (a -> ()) -> NFData a
$crnf :: OrderByClause -> ()
rnf :: OrderByClause -> ()
DeepSeq.NFData)
newtype QueryBuilder (table :: Symbol) = QueryBuilder { forall (table :: Symbol). QueryBuilder table -> SQLQuery
unQueryBuilder :: SQLQuery }
addCondition :: Condition -> QueryBuilder table -> QueryBuilder table
addCondition :: forall (table :: Symbol).
Condition -> QueryBuilder table -> QueryBuilder table
addCondition Condition
condition (QueryBuilder SQLQuery
sq) = SQLQuery -> QueryBuilder table
forall (table :: Symbol). SQLQuery -> QueryBuilder table
QueryBuilder (SQLQuery -> QueryBuilder table) -> SQLQuery -> QueryBuilder table
forall a b. (a -> b) -> a -> b
$ SQLQuery
sq
{ whereCondition = case whereCondition sq of
Maybe Condition
Nothing -> Condition -> Maybe Condition
forall a. a -> Maybe a
Just Condition
condition
Just Condition
existing -> Condition -> Maybe Condition
forall a. a -> Maybe a
Just (Condition -> Condition -> Condition
AndCondition Condition
existing Condition
condition)
}
{-# INLINE addCondition #-}
data ConditionValue
= Param !(Encoders.Params ())
| Literal !Text
instance Show ConditionValue where
showsPrec :: Int -> ConditionValue -> ShowS
showsPrec Int
_ (Param Params ()
_) = String -> ShowS
Prelude.showString String
"<Param>"
showsPrec Int
_ (Literal Text
t) = String -> ShowS
Prelude.showString String
"Literal " ShowS -> ShowS -> ShowS
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Text -> ShowS
forall a. Show a => a -> ShowS
Prelude.shows Text
t
instance Eq ConditionValue where
Literal Text
a == :: ConditionValue -> ConditionValue -> Bool
== Literal Text
b = Text
a Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
b
ConditionValue
_ == ConditionValue
_ = Bool
False
data Condition
= ColumnCondition !Text !FilterOperator !ConditionValue !(Maybe Text) !(Maybe Text)
| OrCondition !Condition !Condition
| AndCondition !Condition !Condition
conditionTag :: Condition -> Int
conditionTag :: Condition -> Int
conditionTag ColumnCondition {} = Int
0
conditionTag OrCondition {} = Int
1
conditionTag AndCondition {} = Int
2
instance Eq Condition where
(ColumnCondition Text
c1 FilterOperator
o1 ConditionValue
s1 Maybe Text
al1 Maybe Text
ar1) == :: Condition -> Condition -> Bool
== (ColumnCondition Text
c2 FilterOperator
o2 ConditionValue
s2 Maybe Text
al2 Maybe Text
ar2) = Text
c1 Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
c2 Bool -> Bool -> Bool
&& FilterOperator
o1 FilterOperator -> FilterOperator -> Bool
forall a. Eq a => a -> a -> Bool
== FilterOperator
o2 Bool -> Bool -> Bool
&& ConditionValue
s1 ConditionValue -> ConditionValue -> Bool
forall a. Eq a => a -> a -> Bool
== ConditionValue
s2 Bool -> Bool -> Bool
&& Maybe Text
al1 Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe Text
al2 Bool -> Bool -> Bool
&& Maybe Text
ar1 Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== Maybe Text
ar2
(OrCondition Condition
l1 Condition
r1) == (OrCondition Condition
l2 Condition
r2) = Condition
l1 Condition -> Condition -> Bool
forall a. Eq a => a -> a -> Bool
== Condition
l2 Bool -> Bool -> Bool
&& Condition
r1 Condition -> Condition -> Bool
forall a. Eq a => a -> a -> Bool
== Condition
r2
(AndCondition Condition
l1 Condition
r1) == (AndCondition Condition
l2 Condition
r2) = Condition
l1 Condition -> Condition -> Bool
forall a. Eq a => a -> a -> Bool
== Condition
l2 Bool -> Bool -> Bool
&& Condition
r1 Condition -> Condition -> Bool
forall a. Eq a => a -> a -> Bool
== Condition
r2
Condition
a == Condition
b = Condition -> Int
conditionTag Condition
a Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Condition -> Int
conditionTag Condition
b
deriving instance Show Condition
instance Show (QueryBuilder table) where
show :: QueryBuilder table -> String
show (QueryBuilder SQLQuery
sq) = String
"QueryBuilder " String -> ShowS
forall {a}. Semigroup a => a -> a -> a
++ SQLQuery -> String
forall a. Show a => a -> String
Prelude.show SQLQuery
sq
instance Eq (QueryBuilder table) where
(QueryBuilder SQLQuery
sq1) == :: QueryBuilder table -> QueryBuilder table -> Bool
== (QueryBuilder SQLQuery
sq2) = SQLQuery -> SQLQuery -> Bool
sqlQueryEq SQLQuery
sq1 SQLQuery
sq2
sqlQueryEq :: SQLQuery -> SQLQuery -> Bool
sqlQueryEq :: SQLQuery -> SQLQuery -> Bool
sqlQueryEq SQLQuery
a SQLQuery
b =
SQLQuery -> Text
selectFrom SQLQuery
a Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== SQLQuery -> Text
selectFrom SQLQuery
b
Bool -> Bool -> Bool
&& SQLQuery -> Text
columnsSql SQLQuery
a Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== SQLQuery -> Text
columnsSql SQLQuery
b
Bool -> Bool -> Bool
&& SQLQuery -> Bool
distinctClause SQLQuery
a Bool -> Bool -> Bool
forall a. Eq a => a -> a -> Bool
== SQLQuery -> Bool
distinctClause SQLQuery
b
Bool -> Bool -> Bool
&& SQLQuery -> Maybe Text
distinctOnClause SQLQuery
a Maybe Text -> Maybe Text -> Bool
forall a. Eq a => a -> a -> Bool
== SQLQuery -> Maybe Text
distinctOnClause SQLQuery
b
Bool -> Bool -> Bool
&& Maybe Condition -> Maybe Condition -> Bool
forall {a}. Eq a => Maybe a -> Maybe a -> Bool
condEq (SQLQuery -> Maybe Condition
whereCondition SQLQuery
a) (SQLQuery -> Maybe Condition
whereCondition SQLQuery
b)
Bool -> Bool -> Bool
&& SQLQuery -> [OrderByClause]
orderByClause SQLQuery
a [OrderByClause] -> [OrderByClause] -> Bool
forall a. Eq a => a -> a -> Bool
== SQLQuery -> [OrderByClause]
orderByClause SQLQuery
b
Bool -> Bool -> Bool
&& SQLQuery -> Maybe Int
limitClause SQLQuery
a Maybe Int -> Maybe Int -> Bool
forall a. Eq a => a -> a -> Bool
== SQLQuery -> Maybe Int
limitClause SQLQuery
b
Bool -> Bool -> Bool
&& SQLQuery -> Maybe Int
offsetClause SQLQuery
a Maybe Int -> Maybe Int -> Bool
forall a. Eq a => a -> a -> Bool
== SQLQuery -> Maybe Int
offsetClause SQLQuery
b
where
condEq :: Maybe a -> Maybe a -> Bool
condEq Maybe a
Nothing Maybe a
Nothing = Bool
True
condEq (Just a
c1) (Just a
c2) = a
c1 a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
c2
condEq Maybe a
_ Maybe a
_ = Bool
False
instance KnownSymbol table => ToHtml (QueryBuilder table) where
toHtml :: QueryBuilder table -> Markup
toHtml QueryBuilder table
queryBuilder = Text -> Markup
forall a. ToHtml a => a -> Markup
toHtml (QueryBuilder table -> Text
toSQLQueryBuilder QueryBuilder table
queryBuilder)
where
toSQLQueryBuilder :: QueryBuilder table -> Text
toSQLQueryBuilder :: QueryBuilder table -> Text
toSQLQueryBuilder QueryBuilder table
qb = Text
"QueryBuilder<" Text -> Text -> Text
forall {a}. Semigroup a => a -> a -> a
<> forall (symbol :: Symbol). KnownSymbol symbol => Text
symbolToText @table Text -> Text -> Text
forall {a}. Semigroup a => a -> a -> a
<> Text
">"
data OrderByDirection = Asc | Desc deriving (OrderByDirection -> OrderByDirection -> Bool
(OrderByDirection -> OrderByDirection -> Bool)
-> (OrderByDirection -> OrderByDirection -> Bool)
-> Eq OrderByDirection
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: OrderByDirection -> OrderByDirection -> Bool
== :: OrderByDirection -> OrderByDirection -> Bool
$c/= :: OrderByDirection -> OrderByDirection -> Bool
/= :: OrderByDirection -> OrderByDirection -> Bool
Eq, Int -> OrderByDirection -> ShowS
[OrderByDirection] -> ShowS
OrderByDirection -> String
(Int -> OrderByDirection -> ShowS)
-> (OrderByDirection -> String)
-> ([OrderByDirection] -> ShowS)
-> Show OrderByDirection
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> OrderByDirection -> ShowS
showsPrec :: Int -> OrderByDirection -> ShowS
$cshow :: OrderByDirection -> String
show :: OrderByDirection -> String
$cshowList :: [OrderByDirection] -> ShowS
showList :: [OrderByDirection] -> ShowS
Show, (forall x. OrderByDirection -> Rep OrderByDirection x)
-> (forall x. Rep OrderByDirection x -> OrderByDirection)
-> Generic OrderByDirection
forall x. Rep OrderByDirection x -> OrderByDirection
forall x. OrderByDirection -> Rep OrderByDirection x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. OrderByDirection -> Rep OrderByDirection x
from :: forall x. OrderByDirection -> Rep OrderByDirection x
$cto :: forall x. Rep OrderByDirection x -> OrderByDirection
to :: forall x. Rep OrderByDirection x -> OrderByDirection
GHC.Generics.Generic, OrderByDirection -> ()
(OrderByDirection -> ()) -> NFData OrderByDirection
forall a. (a -> ()) -> NFData a
$crnf :: OrderByDirection -> ()
rnf :: OrderByDirection -> ()
DeepSeq.NFData)
data SQLQuery = SQLQuery
{ SQLQuery -> Text
selectFrom :: !Text
, SQLQuery -> Bool
distinctClause :: !Bool
, SQLQuery -> Maybe Text
distinctOnClause :: !(Maybe Text)
, SQLQuery -> Maybe Condition
whereCondition :: !(Maybe Condition)
, SQLQuery -> [OrderByClause]
orderByClause :: ![OrderByClause]
, SQLQuery -> Maybe Int
limitClause :: !(Maybe Int)
, SQLQuery -> Maybe Int
offsetClause :: !(Maybe Int)
, SQLQuery -> [Text]
columns :: ![Text]
, SQLQuery -> Text
columnsSql :: !Text
} deriving (Int -> SQLQuery -> ShowS
[SQLQuery] -> ShowS
SQLQuery -> String
(Int -> SQLQuery -> ShowS)
-> (SQLQuery -> String) -> ([SQLQuery] -> ShowS) -> Show SQLQuery
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> SQLQuery -> ShowS
showsPrec :: Int -> SQLQuery -> ShowS
$cshow :: SQLQuery -> String
show :: SQLQuery -> String
$cshowList :: [SQLQuery] -> ShowS
showList :: [SQLQuery] -> ShowS
Show)
instance SetField "selectFrom" SQLQuery Text where setField :: Text -> SQLQuery -> SQLQuery
setField Text
value SQLQuery
sqlQuery = SQLQuery
sqlQuery { selectFrom = value }
instance SetField "distinctClause" SQLQuery Bool where setField :: Bool -> SQLQuery -> SQLQuery
setField Bool
value SQLQuery
sqlQuery = SQLQuery
sqlQuery { distinctClause = value }
instance SetField "distinctOnClause" SQLQuery (Maybe Text) where setField :: Maybe Text -> SQLQuery -> SQLQuery
setField Maybe Text
value SQLQuery
sqlQuery = SQLQuery
sqlQuery { distinctOnClause = value }
instance SetField "whereCondition" SQLQuery (Maybe Condition) where setField :: Maybe Condition -> SQLQuery -> SQLQuery
setField Maybe Condition
value SQLQuery
sqlQuery = SQLQuery
sqlQuery { whereCondition = value }
instance SetField "orderByClause" SQLQuery [OrderByClause] where setField :: [OrderByClause] -> SQLQuery -> SQLQuery
setField [OrderByClause]
value SQLQuery
sqlQuery = SQLQuery
sqlQuery { orderByClause = value }
instance SetField "limitClause" SQLQuery (Maybe Int) where setField :: Maybe Int -> SQLQuery -> SQLQuery
setField Maybe Int
value SQLQuery
sqlQuery = SQLQuery
sqlQuery { limitClause = value }
instance SetField "offsetClause" SQLQuery (Maybe Int) where setField :: Maybe Int -> SQLQuery -> SQLQuery
setField Maybe Int
value SQLQuery
sqlQuery = SQLQuery
sqlQuery { offsetClause = value }
class DefaultScope table where
defaultScope :: QueryBuilder table -> QueryBuilder table
instance {-# OVERLAPPABLE #-} DefaultScope table where
{-# INLINE defaultScope #-}
defaultScope :: QueryBuilder table -> QueryBuilder table
defaultScope QueryBuilder table
queryBuilder = QueryBuilder table
queryBuilder
instance Table (GetModelByTableName table) => Default (QueryBuilder table) where
{-# INLINE def #-}
def :: QueryBuilder table
def = let tn :: Text
tn = forall record. Table record => Text
tableName @(GetModelByTableName table)
cols :: [Text]
cols = forall record. Table record => [Text]
columnNames @(GetModelByTableName table)
in SQLQuery -> QueryBuilder table
forall (table :: Symbol). SQLQuery -> QueryBuilder table
QueryBuilder SQLQuery
{ selectFrom :: Text
selectFrom = Text
tn
, columns :: [Text]
columns = [Text]
cols
, columnsSql :: Text
columnsSql = Text -> [Text] -> Text
qualifyAndJoinColumns Text
tn [Text]
cols
, distinctClause :: Bool
distinctClause = Bool
False
, distinctOnClause :: Maybe Text
distinctOnClause = Maybe Text
forall a. Maybe a
Nothing
, whereCondition :: Maybe Condition
whereCondition = Maybe Condition
forall a. Maybe a
Nothing
, orderByClause :: [OrderByClause]
orderByClause = []
, limitClause :: Maybe Int
limitClause = Maybe Int
forall a. Maybe a
Nothing
, offsetClause :: Maybe Int
offsetClause = Maybe Int
forall a. Maybe a
Nothing
}
qualifyAndJoinColumns :: Text -> [Text] -> Text
qualifyAndJoinColumns :: Text -> [Text] -> Text
qualifyAndJoinColumns Text
tableName [Text]
columns =
Text -> [Text] -> Text
Text.intercalate Text
", " ((Text -> Text) -> [Text] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (\Text
c -> Text
tableName Text -> Text -> Text
forall {a}. Semigroup a => a -> a -> a
<> Text
"." Text -> Text -> Text
forall {a}. Semigroup a => a -> a -> a
<> Text
c) [Text]
columns)
{-# NOINLINE qualifyAndJoinColumns #-}
class EqOrIsOperator value where toEqOrIsOperator :: value -> FilterOperator
instance {-# OVERLAPS #-} EqOrIsOperator (Maybe something) where toEqOrIsOperator :: Maybe something -> FilterOperator
toEqOrIsOperator Maybe something
Nothing = FilterOperator
IsOp; toEqOrIsOperator (Just something
_) = FilterOperator
EqOp
instance {-# OVERLAPPABLE #-} EqOrIsOperator otherwise where toEqOrIsOperator :: otherwise -> FilterOperator
toEqOrIsOperator otherwise
_ = FilterOperator
EqOp
class FilterPrimaryKey table where
filterWhereId :: Id' table -> QueryBuilder table -> QueryBuilder table