2021-12-22 03:10:28 +03:00
|
|
|
{-# LANGUAGE DeriveAnyClass #-}
|
|
|
|
|
2022-02-25 19:08:18 +03:00
|
|
|
module Hasura.Backends.DataWrapper.IR.Expression
|
2021-12-22 03:10:28 +03:00
|
|
|
( Expression (..),
|
|
|
|
Operator (..),
|
|
|
|
)
|
|
|
|
where
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2022-03-31 07:45:03 +03:00
|
|
|
import Autodocodec.Extended
|
2021-12-22 03:10:28 +03:00
|
|
|
import Data.Aeson (FromJSON, ToJSON)
|
2022-03-16 07:12:15 +03:00
|
|
|
import Data.HashSet qualified as S
|
|
|
|
import Hasura.Backends.DataWrapper.API qualified as API
|
2022-02-25 19:08:18 +03:00
|
|
|
import Hasura.Backends.DataWrapper.IR.Column qualified as Column (Name)
|
|
|
|
import Hasura.Backends.DataWrapper.IR.Scalar.Value qualified as Scalar (Value)
|
2021-12-22 03:10:28 +03:00
|
|
|
import Hasura.Incremental (Cacheable)
|
|
|
|
import Hasura.Prelude
|
2022-03-16 07:12:15 +03:00
|
|
|
import Witch
|
2021-12-22 03:10:28 +03:00
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2022-02-25 19:08:18 +03:00
|
|
|
-- | A "concrete" expression type for datasource-agnostic queries (as opposed
|
|
|
|
-- to our existing polymorphic intermediate representation).
|
2021-12-22 03:10:28 +03:00
|
|
|
--
|
|
|
|
-- This type should be seen as an intermediate phase of the processing pipeline
|
|
|
|
-- which provides a high-level interface that the GraphQL Engine can use to
|
|
|
|
-- inspect, manipulate, optimize, etc. before sending off to an agent that will
|
|
|
|
-- be responsible for performing query generation/execution.
|
|
|
|
--
|
|
|
|
-- This type should ascribe clear semantics to its sub-expressions; when this
|
|
|
|
-- is not possible, it should clearly defer to the semantics of some reference
|
|
|
|
-- datasource with clearer documentation.
|
|
|
|
--
|
|
|
|
-- e.g. https://www.postgresql.org/docs/13/sql-expressions.html
|
|
|
|
data Expression
|
|
|
|
= -- | A constant 'Scalar.Value'.
|
|
|
|
Literal Scalar.Value
|
|
|
|
| -- | A construct for making multiple comparisons between groups of
|
|
|
|
-- 'Scalar.Value's.
|
|
|
|
--
|
|
|
|
-- The right-hand side is a collection of unique 'Scalar.Value's; the
|
|
|
|
-- result is "true" if the result of the left-hand 'Expression' is equal to
|
|
|
|
-- any of these 'Scalar.Value's.
|
|
|
|
--
|
|
|
|
-- cf. https://www.postgresql.org/docs/13/functions-comparisons.html#FUNCTIONS-COMPARISONS-IN-SCALAR
|
|
|
|
--
|
2022-02-25 19:08:18 +03:00
|
|
|
-- XXX(jkachmar): It's unclear that there's any benefit from using a
|
2021-12-22 03:10:28 +03:00
|
|
|
-- 'HashSet' for the RHS collection of 'Scalar.Value's.
|
|
|
|
--
|
|
|
|
-- Consider switching this to a 'Set' after the typeclass methods which use
|
|
|
|
-- this type have been implemented and we have an opportunity to see how
|
|
|
|
-- its used in practice.
|
|
|
|
In Expression (HashSet Scalar.Value)
|
|
|
|
| -- | A logical @AND@ operator.
|
|
|
|
--
|
|
|
|
-- cf. https://www.postgresql.org/docs/13/functions-logical.html
|
|
|
|
And [Expression]
|
|
|
|
| -- | A logical @OR@ operator.
|
|
|
|
--
|
|
|
|
-- cf. https://www.postgresql.org/docs/13/functions-logical.html
|
|
|
|
Or [Expression]
|
|
|
|
| -- | A logical @NOT@ operator.
|
|
|
|
--
|
|
|
|
-- cf. https://www.postgresql.org/docs/13/functions-logical.html
|
|
|
|
Not Expression
|
|
|
|
| -- | A comparison predicate which returns "true" if an expression evaluates
|
|
|
|
-- to 'Scalar.Null'.
|
|
|
|
IsNull Expression
|
|
|
|
| -- | A comparison predicate which returns "true" if an expression does not
|
|
|
|
-- evaluate to 'Scalar.Null'.
|
|
|
|
IsNotNull Expression
|
|
|
|
| -- | The textual name associated with some "column" of data within a
|
|
|
|
-- datasource.
|
|
|
|
--
|
|
|
|
-- XXX(jkachmar): It's unclear whether "column" is the right descriptor for
|
|
|
|
-- this construct; what we want here seems closer to an "identifier".
|
|
|
|
--
|
|
|
|
-- cf. https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS
|
|
|
|
Column Column.Name
|
|
|
|
| -- | An equality operation which returns "true" if two expressions evaluate
|
|
|
|
-- to equivalent forms.
|
|
|
|
--
|
|
|
|
-- cf. https://www.postgresql.org/docs/13/functions-comparison.html
|
|
|
|
--
|
|
|
|
-- XXX(jkachmar): Consider making this a part of 'Operator'.
|
|
|
|
--
|
|
|
|
-- XXX(jkachmar): Equality of expressions is tricky business!
|
|
|
|
--
|
|
|
|
-- We should define the semantics of expression equality in a way that is
|
|
|
|
-- clear and carefully considered.
|
|
|
|
Equal Expression Expression
|
|
|
|
| -- | An inequality operation which returns "true" if two expressions do not
|
|
|
|
-- evaluate to equivalent forms.
|
|
|
|
--
|
|
|
|
-- cf. https://www.postgresql.org/docs/13/functions-comparison.html
|
|
|
|
--
|
|
|
|
-- XXX(jkachmar): Consider making this a part of 'Operator', or eliminating
|
|
|
|
-- 'NotEqual' as an explicit case of 'Expression' and only ever construct
|
|
|
|
-- it as @Not (Equal x y)@.
|
|
|
|
--
|
|
|
|
-- XXX(jkachmar): Inequality of expressions is tricky business!
|
|
|
|
--
|
|
|
|
-- We should define the semantics of expression inequality in a way that is
|
|
|
|
-- clear and carefully considered.
|
|
|
|
NotEqual Expression Expression
|
|
|
|
| -- | Apply a comparison 'Operator' to two expressions; the result of this
|
|
|
|
-- application will return "true" or "false" depending on the 'Operator'
|
|
|
|
-- that's being applied.
|
|
|
|
--
|
|
|
|
-- XXX(jkachmar): Consider renaming 'Operator' to @ComparisonOperator@ and
|
|
|
|
-- this sub-expression to @ApplyComparisonOperator@ for clarity.
|
|
|
|
ApplyOperator Operator Expression Expression
|
|
|
|
deriving stock (Data, Eq, Generic, Ord, Show)
|
|
|
|
deriving anyclass (Cacheable, FromJSON, Hashable, NFData, ToJSON)
|
|
|
|
|
2022-03-16 07:12:15 +03:00
|
|
|
instance From API.Expression Expression where
|
|
|
|
from = \case
|
2022-03-31 07:45:03 +03:00
|
|
|
API.Literal (ValueWrapper value) -> Literal $ from value
|
|
|
|
API.In (ValueWrapper2 expr values) -> In (from expr) (S.map from $ S.fromList values)
|
|
|
|
API.And (ValueWrapper exprs) -> And $ map from exprs
|
|
|
|
API.Or (ValueWrapper exprs) -> Or $ map from exprs
|
|
|
|
API.Not (ValueWrapper expr) -> Not $ from expr
|
|
|
|
API.IsNull (ValueWrapper expr) -> IsNull $ from expr
|
|
|
|
API.IsNotNull (ValueWrapper expr) -> IsNotNull $ from expr
|
|
|
|
API.Column (ValueWrapper name) -> Column $ from name
|
|
|
|
API.Equal (ValueWrapper2 expr1 expr2) -> Equal (from expr1) (from expr2)
|
|
|
|
API.NotEqual (ValueWrapper2 expr1 expr2) -> NotEqual (from expr1) (from expr2)
|
|
|
|
API.ApplyOperator (ValueWrapper3 op expr1 expr2) -> ApplyOperator (from op) (from expr1) (from expr2)
|
2022-03-16 07:12:15 +03:00
|
|
|
|
2022-04-08 09:48:37 +03:00
|
|
|
instance From Expression API.Expression where
|
|
|
|
from = \case
|
|
|
|
Literal value -> API.Literal $ ValueWrapper $ from value
|
|
|
|
In expr values -> API.In (ValueWrapper2 (from expr) (map from . S.toList $ values))
|
|
|
|
And exprs -> API.And (ValueWrapper (map from exprs))
|
|
|
|
Or exprs -> API.Or (ValueWrapper (map from exprs))
|
|
|
|
Not expr -> API.Not (ValueWrapper (from expr))
|
|
|
|
IsNull expr -> API.IsNull (ValueWrapper (from expr))
|
|
|
|
IsNotNull expr -> API.IsNotNull (ValueWrapper (from expr))
|
|
|
|
Column name -> API.Column (ValueWrapper (from name))
|
|
|
|
Equal expr1 expr2 -> API.Equal (ValueWrapper2 (from expr1) (from expr2))
|
|
|
|
NotEqual expr1 expr2 -> API.NotEqual (ValueWrapper2 (from expr1) (from expr2))
|
|
|
|
ApplyOperator op expr1 expr2 -> API.ApplyOperator (ValueWrapper3 (from op) (from expr1) (from expr2))
|
|
|
|
|
2021-12-22 03:10:28 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
-- | Operators which are typically applied to two 'Expression's (via the
|
|
|
|
-- 'ApplyOperator' sub-'Expression') to perform a boolean comparison.
|
|
|
|
--
|
|
|
|
-- cf. https://www.postgresql.org/docs/13/functions-comparison.html
|
|
|
|
--
|
|
|
|
-- XXX(jkachmar): Comparison operations are tricky business!
|
|
|
|
--
|
|
|
|
-- We should define the semantics of these comparisons in a way that is clear
|
|
|
|
-- and carefully considered.
|
|
|
|
data Operator
|
|
|
|
= LessThan
|
|
|
|
| LessThanOrEqual
|
|
|
|
| GreaterThan
|
|
|
|
| GreaterThanOrEqual
|
|
|
|
deriving stock (Data, Eq, Generic, Ord, Show)
|
|
|
|
deriving anyclass (Cacheable, FromJSON, Hashable, NFData, ToJSON)
|
2022-03-16 07:12:15 +03:00
|
|
|
|
|
|
|
instance From API.Operator Operator where
|
|
|
|
from API.LessThan = LessThan
|
|
|
|
from API.LessThanOrEqual = LessThanOrEqual
|
|
|
|
from API.GreaterThan = GreaterThan
|
|
|
|
from API.GreaterThanOrEqual = GreaterThanOrEqual
|
2022-04-08 09:48:37 +03:00
|
|
|
|
|
|
|
instance From Operator API.Operator where
|
|
|
|
from LessThan = API.LessThan
|
|
|
|
from LessThanOrEqual = API.LessThanOrEqual
|
|
|
|
from GreaterThan = API.GreaterThan
|
|
|
|
from GreaterThanOrEqual = API.GreaterThanOrEqual
|