{-# LANGUAGE DeriveAnyClass #-}
{-|
Module: IHP.DataSync.DynamicQuery
Description: The normal IHP query functionality is type-safe. This module provides type-unsafe access to the database.
Copyright: (c) digitally induced GmbH, 2021
-}
module IHP.DataSync.DynamicQuery where

import IHP.ControllerPrelude hiding (OrderByClause)
import Data.Aeson
import qualified Data.HashMap.Strict as HashMap
import qualified Database.PostgreSQL.Simple as PG
import qualified Database.PostgreSQL.Simple.FromField as PG
import qualified Database.PostgreSQL.Simple.FromRow as PG
import qualified Database.PostgreSQL.Simple.ToField as PG
import qualified Database.PostgreSQL.Simple.Types as PG
import qualified Database.PostgreSQL.Simple.Notification as PG
import qualified IHP.QueryBuilder as QueryBuilder
import Data.Aeson.TH
import qualified GHC.Generics
import qualified Control.DeepSeq as DeepSeq

data Field = Field { Field -> Text
fieldName :: Text, Field -> DynamicValue
fieldValue :: DynamicValue }

data DynamicValue
    = IntValue !Int
    | DoubleValue !Double
    | TextValue !Text
    | BoolValue !Bool
    | UUIDValue !UUID
    | DateTimeValue !UTCTime
    | PointValue !Point
    | Null
    deriving (Int -> DynamicValue -> ShowS
[DynamicValue] -> ShowS
DynamicValue -> String
(Int -> DynamicValue -> ShowS)
-> (DynamicValue -> String)
-> ([DynamicValue] -> ShowS)
-> Show DynamicValue
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DynamicValue] -> ShowS
$cshowList :: [DynamicValue] -> ShowS
show :: DynamicValue -> String
$cshow :: DynamicValue -> String
showsPrec :: Int -> DynamicValue -> ShowS
$cshowsPrec :: Int -> DynamicValue -> ShowS
Show, DynamicValue -> DynamicValue -> Bool
(DynamicValue -> DynamicValue -> Bool)
-> (DynamicValue -> DynamicValue -> Bool) -> Eq DynamicValue
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DynamicValue -> DynamicValue -> Bool
$c/= :: DynamicValue -> DynamicValue -> Bool
== :: DynamicValue -> DynamicValue -> Bool
$c== :: DynamicValue -> DynamicValue -> Bool
Eq)

newtype UndecodedJSON = UndecodedJSON ByteString
    deriving (Int -> UndecodedJSON -> ShowS
[UndecodedJSON] -> ShowS
UndecodedJSON -> String
(Int -> UndecodedJSON -> ShowS)
-> (UndecodedJSON -> String)
-> ([UndecodedJSON] -> ShowS)
-> Show UndecodedJSON
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [UndecodedJSON] -> ShowS
$cshowList :: [UndecodedJSON] -> ShowS
show :: UndecodedJSON -> String
$cshow :: UndecodedJSON -> String
showsPrec :: Int -> UndecodedJSON -> ShowS
$cshowsPrec :: Int -> UndecodedJSON -> ShowS
Show, UndecodedJSON -> UndecodedJSON -> Bool
(UndecodedJSON -> UndecodedJSON -> Bool)
-> (UndecodedJSON -> UndecodedJSON -> Bool) -> Eq UndecodedJSON
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: UndecodedJSON -> UndecodedJSON -> Bool
$c/= :: UndecodedJSON -> UndecodedJSON -> Bool
== :: UndecodedJSON -> UndecodedJSON -> Bool
$c== :: UndecodedJSON -> UndecodedJSON -> Bool
Eq)

-- | Similiar to IHP.QueryBuilder.SQLQuery, but is designed to be accessed by external users
--
-- When compiling to SQL we have to be extra careful to escape all identifers and variables in the query.
-- The normal IHP.QueryBuilder doesn't need to be that careful as parts of the input are derived from
-- generated code from the Schema.sql.
--
data DynamicSQLQuery = DynamicSQLQuery
    { DynamicSQLQuery -> Text
table :: !Text
    , DynamicSQLQuery -> SelectedColumns
selectedColumns :: SelectedColumns
    , DynamicSQLQuery -> Maybe ConditionExpression
whereCondition :: !(Maybe ConditionExpression)
    , DynamicSQLQuery -> [OrderByClause]
orderByClause :: ![OrderByClause]
    , DynamicSQLQuery -> Maybe ByteString
distinctOnColumn :: !(Maybe ByteString)
    , DynamicSQLQuery -> Maybe Int
limit :: !(Maybe Int)
    , DynamicSQLQuery -> Maybe Int
offset :: !(Maybe Int)
    } deriving (Int -> DynamicSQLQuery -> ShowS
[DynamicSQLQuery] -> ShowS
DynamicSQLQuery -> String
(Int -> DynamicSQLQuery -> ShowS)
-> (DynamicSQLQuery -> String)
-> ([DynamicSQLQuery] -> ShowS)
-> Show DynamicSQLQuery
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [DynamicSQLQuery] -> ShowS
$cshowList :: [DynamicSQLQuery] -> ShowS
show :: DynamicSQLQuery -> String
$cshow :: DynamicSQLQuery -> String
showsPrec :: Int -> DynamicSQLQuery -> ShowS
$cshowsPrec :: Int -> DynamicSQLQuery -> ShowS
Show, DynamicSQLQuery -> DynamicSQLQuery -> Bool
(DynamicSQLQuery -> DynamicSQLQuery -> Bool)
-> (DynamicSQLQuery -> DynamicSQLQuery -> Bool)
-> Eq DynamicSQLQuery
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: DynamicSQLQuery -> DynamicSQLQuery -> Bool
$c/= :: DynamicSQLQuery -> DynamicSQLQuery -> Bool
== :: DynamicSQLQuery -> DynamicSQLQuery -> Bool
$c== :: DynamicSQLQuery -> DynamicSQLQuery -> Bool
Eq)

data OrderByClause
    = OrderByClause
        { OrderByClause -> ByteString
orderByColumn :: !ByteString
        , OrderByClause -> OrderByDirection
orderByDirection :: !OrderByDirection }
    | OrderByTSRank { OrderByClause -> Text
tsvector :: Text, OrderByClause -> Text
tsquery :: !Text }
    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
showList :: [OrderByClause] -> ShowS
$cshowList :: [OrderByClause] -> ShowS
show :: OrderByClause -> String
$cshow :: OrderByClause -> String
showsPrec :: Int -> OrderByClause -> ShowS
$cshowsPrec :: Int -> OrderByClause -> ShowS
Show, OrderByClause -> OrderByClause -> Bool
(OrderByClause -> OrderByClause -> Bool)
-> (OrderByClause -> OrderByClause -> Bool) -> Eq OrderByClause
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: OrderByClause -> OrderByClause -> Bool
$c/= :: OrderByClause -> OrderByClause -> Bool
== :: OrderByClause -> OrderByClause -> Bool
$c== :: 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
$cto :: forall x. Rep OrderByClause x -> OrderByClause
$cfrom :: forall x. OrderByClause -> Rep OrderByClause x
GHC.Generics.Generic, OrderByClause -> ()
(OrderByClause -> ()) -> NFData OrderByClause
forall a. (a -> ()) -> NFData a
rnf :: OrderByClause -> ()
$crnf :: OrderByClause -> ()
DeepSeq.NFData)

-- | Represents a WHERE conditions of a 'DynamicSQLQuery'
data ConditionExpression
    = ColumnExpression { ConditionExpression -> Text
field :: !Text }
    | NullExpression
    | InfixOperatorExpression
        { ConditionExpression -> ConditionExpression
left :: !ConditionExpression
        , ConditionExpression -> ConditionOperator
op :: !ConditionOperator
        , ConditionExpression -> ConditionExpression
right :: !ConditionExpression
        }
    | LiteralExpression { ConditionExpression -> DynamicValue
value :: !DynamicValue }
    | CallExpression { ConditionExpression -> FunctionCall
functionCall :: !FunctionCall }
    | ListExpression { ConditionExpression -> [DynamicValue]
values :: ![DynamicValue] }
    deriving (Int -> ConditionExpression -> ShowS
[ConditionExpression] -> ShowS
ConditionExpression -> String
(Int -> ConditionExpression -> ShowS)
-> (ConditionExpression -> String)
-> ([ConditionExpression] -> ShowS)
-> Show ConditionExpression
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ConditionExpression] -> ShowS
$cshowList :: [ConditionExpression] -> ShowS
show :: ConditionExpression -> String
$cshow :: ConditionExpression -> String
showsPrec :: Int -> ConditionExpression -> ShowS
$cshowsPrec :: Int -> ConditionExpression -> ShowS
Show, ConditionExpression -> ConditionExpression -> Bool
(ConditionExpression -> ConditionExpression -> Bool)
-> (ConditionExpression -> ConditionExpression -> Bool)
-> Eq ConditionExpression
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ConditionExpression -> ConditionExpression -> Bool
$c/= :: ConditionExpression -> ConditionExpression -> Bool
== :: ConditionExpression -> ConditionExpression -> Bool
$c== :: ConditionExpression -> ConditionExpression -> Bool
Eq)

data FunctionCall
    = ToTSQuery { FunctionCall -> Text
text :: !Text } -- ^ to_tsquery('english', text)
    deriving (Int -> FunctionCall -> ShowS
[FunctionCall] -> ShowS
FunctionCall -> String
(Int -> FunctionCall -> ShowS)
-> (FunctionCall -> String)
-> ([FunctionCall] -> ShowS)
-> Show FunctionCall
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [FunctionCall] -> ShowS
$cshowList :: [FunctionCall] -> ShowS
show :: FunctionCall -> String
$cshow :: FunctionCall -> String
showsPrec :: Int -> FunctionCall -> ShowS
$cshowsPrec :: Int -> FunctionCall -> ShowS
Show, FunctionCall -> FunctionCall -> Bool
(FunctionCall -> FunctionCall -> Bool)
-> (FunctionCall -> FunctionCall -> Bool) -> Eq FunctionCall
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: FunctionCall -> FunctionCall -> Bool
$c/= :: FunctionCall -> FunctionCall -> Bool
== :: FunctionCall -> FunctionCall -> Bool
$c== :: FunctionCall -> FunctionCall -> Bool
Eq, (forall x. FunctionCall -> Rep FunctionCall x)
-> (forall x. Rep FunctionCall x -> FunctionCall)
-> Generic FunctionCall
forall x. Rep FunctionCall x -> FunctionCall
forall x. FunctionCall -> Rep FunctionCall x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep FunctionCall x -> FunctionCall
$cfrom :: forall x. FunctionCall -> Rep FunctionCall x
GHC.Generics.Generic, FunctionCall -> ()
(FunctionCall -> ()) -> NFData FunctionCall
forall a. (a -> ()) -> NFData a
rnf :: FunctionCall -> ()
$crnf :: FunctionCall -> ()
DeepSeq.NFData)

-- | Operators available in WHERE conditions
data ConditionOperator
    = OpEqual -- ^ a = b
    | OpGreaterThan -- ^ a > b
    | OpLessThan -- ^ a < b
    | OpGreaterThanOrEqual -- ^ a >= b
    | OpLessThanOrEqual -- ^ a <= b
    | OpNotEqual -- ^ a <> b
    | OpAnd -- ^ a AND b
    | OpOr -- ^ a OR b
    | OpIs -- ^ a IS b
    | OpIsNot -- ^ a IS NOT b
    | OpTSMatch -- ^ tsvec_a @@ tsvec_b
    | OpIn -- ^ a IN b
    deriving (Int -> ConditionOperator -> ShowS
[ConditionOperator] -> ShowS
ConditionOperator -> String
(Int -> ConditionOperator -> ShowS)
-> (ConditionOperator -> String)
-> ([ConditionOperator] -> ShowS)
-> Show ConditionOperator
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [ConditionOperator] -> ShowS
$cshowList :: [ConditionOperator] -> ShowS
show :: ConditionOperator -> String
$cshow :: ConditionOperator -> String
showsPrec :: Int -> ConditionOperator -> ShowS
$cshowsPrec :: Int -> ConditionOperator -> ShowS
Show, ConditionOperator -> ConditionOperator -> Bool
(ConditionOperator -> ConditionOperator -> Bool)
-> (ConditionOperator -> ConditionOperator -> Bool)
-> Eq ConditionOperator
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: ConditionOperator -> ConditionOperator -> Bool
$c/= :: ConditionOperator -> ConditionOperator -> Bool
== :: ConditionOperator -> ConditionOperator -> Bool
$c== :: ConditionOperator -> ConditionOperator -> Bool
Eq)

data SelectedColumns
    = SelectAll -- ^ SELECT * FROM table
    | SelectSpecific [Text] -- ^ SELECT a, b, c FROM table
    deriving (Int -> SelectedColumns -> ShowS
[SelectedColumns] -> ShowS
SelectedColumns -> String
(Int -> SelectedColumns -> ShowS)
-> (SelectedColumns -> String)
-> ([SelectedColumns] -> ShowS)
-> Show SelectedColumns
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [SelectedColumns] -> ShowS
$cshowList :: [SelectedColumns] -> ShowS
show :: SelectedColumns -> String
$cshow :: SelectedColumns -> String
showsPrec :: Int -> SelectedColumns -> ShowS
$cshowsPrec :: Int -> SelectedColumns -> ShowS
Show, SelectedColumns -> SelectedColumns -> Bool
(SelectedColumns -> SelectedColumns -> Bool)
-> (SelectedColumns -> SelectedColumns -> Bool)
-> Eq SelectedColumns
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: SelectedColumns -> SelectedColumns -> Bool
$c/= :: SelectedColumns -> SelectedColumns -> Bool
== :: SelectedColumns -> SelectedColumns -> Bool
$c== :: SelectedColumns -> SelectedColumns -> Bool
Eq)

instance FromJSON ByteString where
    parseJSON :: Value -> Parser ByteString
parseJSON (String Text
v) = ByteString -> Parser ByteString
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Parser ByteString)
-> ByteString -> Parser ByteString
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs Text
v

instance FromJSON PG.Action where
    parseJSON :: Value -> Parser Action
parseJSON (String Text
v) = Action -> Parser Action
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> Action
PG.Escape (Text -> ByteString
forall a b. ConvertibleStrings a b => a -> b
cs Text
v))

instance {-# OVERLAPS #-} ToJSON [Field] where
    toJSON :: [Field] -> Value
toJSON [Field]
fields = [Pair] -> Value
object ((Field -> Pair) -> [Field] -> [Pair]
forall a b. (a -> b) -> [a] -> [b]
map (\Field { Text
fieldName :: Text
$sel:fieldName:Field :: Field -> Text
fieldName, DynamicValue
fieldValue :: DynamicValue
$sel:fieldValue:Field :: Field -> DynamicValue
fieldValue } -> (Text -> Text
forall a b. ConvertibleStrings a b => a -> b
cs Text
fieldName) Text -> Value -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= (DynamicValue -> Value
forall a. ToJSON a => a -> Value
toJSON DynamicValue
fieldValue)) [Field]
fields)
    toEncoding :: [Field] -> Encoding
toEncoding [Field]
fields = Series -> Encoding
pairs (Series -> Encoding) -> Series -> Encoding
forall a b. (a -> b) -> a -> b
$ (Series -> Series -> Series) -> Series -> [Series] -> Series
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Series -> Series -> Series
forall a. Semigroup a => a -> a -> a
(<>) Series
forall a. Monoid a => a
mempty [Series]
encodedFields
        where
            encodedFields :: [Series]
encodedFields = ((Field -> Series) -> [Field] -> [Series]
forall a b. (a -> b) -> [a] -> [b]
map (\Field { Text
fieldName :: Text
$sel:fieldName:Field :: Field -> Text
fieldName, DynamicValue
fieldValue :: DynamicValue
$sel:fieldValue:Field :: Field -> DynamicValue
fieldValue } -> (Text -> Text
forall a b. ConvertibleStrings a b => a -> b
cs Text
fieldName) Text -> Value -> Series
forall kv v. (KeyValue kv, ToJSON v) => Text -> v -> kv
.= (DynamicValue -> Value
forall a. ToJSON a => a -> Value
toJSON DynamicValue
fieldValue)) [Field]
fields)

instance ToJSON DynamicValue where
    toJSON :: DynamicValue -> Value
toJSON (IntValue Int
value) = Int -> Value
forall a. ToJSON a => a -> Value
toJSON Int
value
    toJSON (DoubleValue Double
value) = Double -> Value
forall a. ToJSON a => a -> Value
toJSON Double
value
    toJSON (TextValue Text
value) = Text -> Value
forall a. ToJSON a => a -> Value
toJSON Text
value
    toJSON (BoolValue Bool
value) = Bool -> Value
forall a. ToJSON a => a -> Value
toJSON Bool
value
    toJSON (UUIDValue UUID
value) = UUID -> Value
forall a. ToJSON a => a -> Value
toJSON UUID
value
    toJSON (DateTimeValue UTCTime
value) = UTCTime -> Value
forall a. ToJSON a => a -> Value
toJSON UTCTime
value
    toJSON (PointValue Point
value) = Point -> Value
forall a. ToJSON a => a -> Value
toJSON Point
value
    toJSON DynamicValue
IHP.DataSync.DynamicQuery.Null = Value -> Value
forall a. ToJSON a => a -> Value
toJSON Value
Data.Aeson.Null

instance PG.FromField Field where
    fromField :: FieldParser Field
fromField Field
field Maybe ByteString
fieldValue' = do
            DynamicValue
fieldValue <- Conversion DynamicValue
fieldValue
            Field -> Conversion Field
forall (f :: * -> *) a. Applicative f => a -> f a
pure Field :: Text -> DynamicValue -> Field
Field { Text
DynamicValue
fieldName :: Text
fieldValue :: DynamicValue
$sel:fieldValue:Field :: DynamicValue
$sel:fieldName:Field :: Text
.. }
        where
            fieldName :: Text
fieldName = (Field -> Maybe ByteString
PG.name Field
field)
                Maybe ByteString -> (Maybe ByteString -> Maybe Text) -> Maybe Text
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> (ByteString -> Text) -> Maybe ByteString -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> Text
columnNameToFieldName (Text -> Text) -> (ByteString -> Text) -> ByteString -> Text
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs)
                Maybe Text -> (Maybe Text -> Text) -> Text
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> Text -> Maybe Text -> Text
forall a. a -> Maybe a -> a
fromMaybe Text
""
            fieldValue :: Conversion DynamicValue
fieldValue =
                    (Int -> DynamicValue
IntValue (Int -> DynamicValue) -> Conversion Int -> Conversion DynamicValue
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FieldParser Int
forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                Conversion DynamicValue
-> Conversion DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Text -> DynamicValue
TextValue (Text -> DynamicValue)
-> Conversion Text -> Conversion DynamicValue
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FieldParser Text
forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                Conversion DynamicValue
-> Conversion DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Bool -> DynamicValue
BoolValue (Bool -> DynamicValue)
-> Conversion Bool -> Conversion DynamicValue
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FieldParser Bool
forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                Conversion DynamicValue
-> Conversion DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (UUID -> DynamicValue
UUIDValue (UUID -> DynamicValue)
-> Conversion UUID -> Conversion DynamicValue
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FieldParser UUID
forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                Conversion DynamicValue
-> Conversion DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Double -> DynamicValue
DoubleValue (Double -> DynamicValue)
-> Conversion Double -> Conversion DynamicValue
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FieldParser Double
forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                Conversion DynamicValue
-> Conversion DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (UTCTime -> DynamicValue
DateTimeValue (UTCTime -> DynamicValue)
-> Conversion UTCTime -> Conversion DynamicValue
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FieldParser UTCTime
forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                Conversion DynamicValue
-> Conversion DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (Point -> DynamicValue
PointValue (Point -> DynamicValue)
-> Conversion Point -> Conversion DynamicValue
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FieldParser Point
forall a. FromField a => FieldParser a
PG.fromField Field
field Maybe ByteString
fieldValue')
                Conversion DynamicValue
-> Conversion DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> (FieldParser Null
forall a. FromField a => FieldParser a
PG.fromField @PG.Null Field
field Maybe ByteString
fieldValue' Conversion Null
-> Conversion DynamicValue -> Conversion DynamicValue
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Applicative f => a -> f a
pure DynamicValue
IHP.DataSync.DynamicQuery.Null)
                Conversion DynamicValue
-> Conversion DynamicValue -> Conversion DynamicValue
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Field -> Maybe ByteString -> Conversion DynamicValue
forall (f :: * -> *) a p.
(Applicative f, ConvertibleStrings a Text) =>
p -> Maybe a -> f DynamicValue
fromFieldCustomEnum Field
field Maybe ByteString
fieldValue'

            fromFieldCustomEnum :: p -> Maybe a -> f DynamicValue
fromFieldCustomEnum p
field (Just a
value) = DynamicValue -> f DynamicValue
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> DynamicValue
TextValue (a -> Text
forall a b. ConvertibleStrings a b => a -> b
cs a
value))
            fromFieldCustomEnum p
field Maybe a
Nothing      = DynamicValue -> f DynamicValue
forall (f :: * -> *) a. Applicative f => a -> f a
pure DynamicValue
IHP.DataSync.DynamicQuery.Null

instance PG.FromField UndecodedJSON where
    fromField :: FieldParser UndecodedJSON
fromField Field
field (Just ByteString
value) = UndecodedJSON -> Conversion UndecodedJSON
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> UndecodedJSON
UndecodedJSON ByteString
value)
    fromField Field
field Maybe ByteString
Nothing = UndecodedJSON -> Conversion UndecodedJSON
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ByteString -> UndecodedJSON
UndecodedJSON ByteString
"null")

-- | Returns a list of all id's in a result
recordIds :: [[Field]] -> [UUID]
recordIds :: [[Field]] -> [UUID]
recordIds [[Field]]
result = [[Field]]
result
        [[Field]] -> ([[Field]] -> [Field]) -> [Field]
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> [[Field]] -> [Field]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
        [Field] -> ([Field] -> [Field]) -> [Field]
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> (Field -> Bool) -> [Field] -> [Field]
forall a. (a -> Bool) -> [a] -> [a]
filter (\Field { Text
fieldName :: Text
$sel:fieldName:Field :: Field -> Text
fieldName } -> Text
fieldName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"id")
        [Field] -> ([Field] -> [DynamicValue]) -> [DynamicValue]
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> (Field -> DynamicValue) -> [Field] -> [DynamicValue]
forall a b. (a -> b) -> [a] -> [b]
map (Proxy "fieldValue" -> Field -> DynamicValue
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "fieldValue" (Proxy "fieldValue")
Proxy "fieldValue"
#fieldValue)
        [DynamicValue] -> ([DynamicValue] -> [UUID]) -> [UUID]
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> (DynamicValue -> Maybe UUID) -> [DynamicValue] -> [UUID]
forall a b. (a -> Maybe b) -> [a] -> [b]
mapMaybe \case
            UUIDValue UUID
uuid -> UUID -> Maybe UUID
forall a. a -> Maybe a
Just UUID
uuid
            DynamicValue
otherwise      -> Maybe UUID
forall a. Maybe a
Nothing



-- Here you can add functions which are available in all your controllers

-- | Transforms the keys of a JSON object from field name to column name
--
-- >>> transformColumnNamesToFieldNames [json|{"isCompleted": true}|]
-- [json|{"is_completed": true}|]
transformColumnNamesToFieldNames :: Value -> Value
transformColumnNamesToFieldNames :: Value -> Value
transformColumnNamesToFieldNames (Object Object
hashMap) =
        Object
hashMap
        Object -> (Object -> [Pair]) -> [Pair]
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> Object -> [Pair]
forall k v. HashMap k v -> [(k, v)]
HashMap.toList
        [Pair] -> ([Pair] -> [Pair]) -> [Pair]
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> (Pair -> Pair) -> [Pair] -> [Pair]
forall a b. (a -> b) -> [a] -> [b]
map (\(Text
key, Value
value) -> (Text -> Text
columnNameToFieldName Text
key, Value
value))
        [Pair] -> ([Pair] -> Object) -> Object
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> [Pair] -> Object
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList
        Object -> (Object -> Value) -> Value
forall t1 t2. t1 -> (t1 -> t2) -> t2
|> Object -> Value
Object


$(deriveFromJSON defaultOptions 'QueryBuilder.OrCondition)
$(deriveFromJSON defaultOptions 'QueryBuilder.Join)
$(deriveFromJSON defaultOptions 'QueryBuilder.OrderByClause)
$(deriveFromJSON defaultOptions 'QueryBuilder.Asc)
$(deriveFromJSON defaultOptions 'SelectAll)
$(deriveFromJSON defaultOptions ''ConditionOperator)
$(deriveFromJSON defaultOptions ''ConditionExpression)
$(deriveFromJSON defaultOptions ''DynamicValue)
$(deriveFromJSON defaultOptions ''FunctionCall)

instance FromJSON DynamicSQLQuery where
    parseJSON :: Value -> Parser DynamicSQLQuery
parseJSON = String
-> (Object -> Parser DynamicSQLQuery)
-> Value
-> Parser DynamicSQLQuery
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"DynamicSQLQuery" ((Object -> Parser DynamicSQLQuery)
 -> Value -> Parser DynamicSQLQuery)
-> (Object -> Parser DynamicSQLQuery)
-> Value
-> Parser DynamicSQLQuery
forall a b. (a -> b) -> a -> b
$ \Object
v -> Text
-> SelectedColumns
-> Maybe ConditionExpression
-> [OrderByClause]
-> Maybe ByteString
-> Maybe Int
-> Maybe Int
-> DynamicSQLQuery
DynamicSQLQuery
        (Text
 -> SelectedColumns
 -> Maybe ConditionExpression
 -> [OrderByClause]
 -> Maybe ByteString
 -> Maybe Int
 -> Maybe Int
 -> DynamicSQLQuery)
-> Parser Text
-> Parser
     (SelectedColumns
      -> Maybe ConditionExpression
      -> [OrderByClause]
      -> Maybe ByteString
      -> Maybe Int
      -> Maybe Int
      -> DynamicSQLQuery)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Text -> Parser Text
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"table"
        Parser
  (SelectedColumns
   -> Maybe ConditionExpression
   -> [OrderByClause]
   -> Maybe ByteString
   -> Maybe Int
   -> Maybe Int
   -> DynamicSQLQuery)
-> Parser SelectedColumns
-> Parser
     (Maybe ConditionExpression
      -> [OrderByClause]
      -> Maybe ByteString
      -> Maybe Int
      -> Maybe Int
      -> DynamicSQLQuery)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Text -> Parser SelectedColumns
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"selectedColumns"
        Parser
  (Maybe ConditionExpression
   -> [OrderByClause]
   -> Maybe ByteString
   -> Maybe Int
   -> Maybe Int
   -> DynamicSQLQuery)
-> Parser (Maybe ConditionExpression)
-> Parser
     ([OrderByClause]
      -> Maybe ByteString -> Maybe Int -> Maybe Int -> DynamicSQLQuery)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Text -> Parser (Maybe ConditionExpression)
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"whereCondition"
        Parser
  ([OrderByClause]
   -> Maybe ByteString -> Maybe Int -> Maybe Int -> DynamicSQLQuery)
-> Parser [OrderByClause]
-> Parser
     (Maybe ByteString -> Maybe Int -> Maybe Int -> DynamicSQLQuery)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Text -> Parser [OrderByClause]
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"orderByClause"
        Parser
  (Maybe ByteString -> Maybe Int -> Maybe Int -> DynamicSQLQuery)
-> Parser (Maybe ByteString)
-> Parser (Maybe Int -> Maybe Int -> DynamicSQLQuery)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Text -> Parser (Maybe ByteString)
forall a. FromJSON a => Object -> Text -> Parser (Maybe a)
.:? Text
"distinctOnColumn" -- distinctOnColumn can be absent in older versions of ihp-datasync.js
        Parser (Maybe Int -> Maybe Int -> DynamicSQLQuery)
-> Parser (Maybe Int) -> Parser (Maybe Int -> DynamicSQLQuery)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Text -> Parser (Maybe Int)
forall a. FromJSON a => Object -> Text -> Parser (Maybe a)
.:? Text
"limit" -- Limit can be absent in older versions of ihp-datasync.js
        Parser (Maybe Int -> DynamicSQLQuery)
-> Parser (Maybe Int) -> Parser DynamicSQLQuery
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Text -> Parser (Maybe Int)
forall a. FromJSON a => Object -> Text -> Parser (Maybe a)
.:? Text
"offset" -- Offset can be absent in older versions of ihp-datasync.js


instance FromJSON OrderByClause where
    parseJSON :: Value -> Parser OrderByClause
parseJSON = String
-> (Object -> Parser OrderByClause)
-> Value
-> Parser OrderByClause
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"OrderByClause" ((Object -> Parser OrderByClause) -> Value -> Parser OrderByClause)
-> (Object -> Parser OrderByClause)
-> Value
-> Parser OrderByClause
forall a b. (a -> b) -> a -> b
$ \Object
v -> do
        let oldFormat :: Parser OrderByClause
oldFormat = ByteString -> OrderByDirection -> OrderByClause
OrderByClause
                (ByteString -> OrderByDirection -> OrderByClause)
-> Parser ByteString -> Parser (OrderByDirection -> OrderByClause)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Text -> Parser ByteString
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"orderByColumn"
                Parser (OrderByDirection -> OrderByClause)
-> Parser OrderByDirection -> Parser OrderByClause
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Text -> Parser OrderByDirection
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"orderByDirection"
        let tagged :: Parser OrderByClause
tagged = do
                Text
tag <- Object
v Object -> Text -> Parser Text
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"tag"
                case Text
tag of
                    Text
"OrderByClause" -> Parser OrderByClause
oldFormat
                    Text
"OrderByTSRank" -> Text -> Text -> OrderByClause
OrderByTSRank (Text -> Text -> OrderByClause)
-> Parser Text -> Parser (Text -> OrderByClause)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
v Object -> Text -> Parser Text
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"tsvector" Parser (Text -> OrderByClause)
-> Parser Text -> Parser OrderByClause
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
v Object -> Text -> Parser Text
forall a. FromJSON a => Object -> Text -> Parser a
.: Text
"tsquery"
                    Text
otherwise -> Text -> Parser OrderByClause
forall a. Text -> a
error (Text
"Invalid tag: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
otherwise)
        Parser OrderByClause
tagged Parser OrderByClause
-> Parser OrderByClause -> Parser OrderByClause
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser OrderByClause
oldFormat