mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-18 04:51:35 +03:00
26dfa3e718
## Description This is the first step in making use of Logical Models with document databases such as MongoDB. As part of schema introspection, a data connector agent can supply a set of custom types that can be used to describe the schema for columns within the tables of the database (or _fields_ within a _document collection_ in MongoDB terminology). Previously, we were storing these custom types as `TableObjectType`s within the `TableCoreInfo` for each table. In this PR we - replace the `TableObjectTypes` with `LogicalModel` types - store these directly within the `DBObjectsIntrospection` instead of within the `TableCoreInfo` for each table. (The custom types are shared at the source level so there was no reason to have a separate set of types for each table.) - When building the `SourceInfo`, we combine the `LogicalModel`s from `DBObjectsIntrospection` with `LogicalModel`s from the user's metadata to create the set of `LogicalModels` in the `SourceInfo` within the `SchemaCache`. I.e. we combine the set of types obtained by database introspection with the set of types specified by the user in the metadata. If two types have the same name, we use the type defined in the metadata. ## Limitations and future work - Provide a way for the user to associate a meta-data defined `LogicalModel` with a table instead of requiring one to be provided by DB introspection - Provide a way for the user to edit the `LogicalModel` types provided by introspection and add them to the metadata. - Allow a `LogicalModel` object type to describe and entire table rather than just individual columns. - Better handling for "unknown" types, e.g. if the type of a collection (or part of a collection) is unknown we should treat it as a JSON scalar value. This may also involve adding an `_everything` field which returns the full document as a JSON scalar. PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9345 GitOrigin-RevId: 5cec72fc1be1380d8600f7be547bbf71aad770bd
755 lines
24 KiB
Haskell
755 lines
24 KiB
Haskell
{-# LANGUAGE DeriveAnyClass #-}
|
||
{-# LANGUAGE DuplicateRecordFields #-}
|
||
{-# LANGUAGE UndecidableInstances #-}
|
||
|
||
-- | This modules defines the tree of Select types: how we represent a query internally, from its top
|
||
-- level 'QueryDB' down to each individual field. Most of those types have three type arguments:
|
||
--
|
||
-- b: BackendType
|
||
-- The backend that is targeted by that specific select (Postgres Vanilla, MSSQL...); we use the
|
||
-- type families in the Backend class to decide how different parts of the IR are represented in
|
||
-- different backends.
|
||
--
|
||
-- v: Type
|
||
-- The type of the leaf values in our AST; used almost exclusively for column values, over which
|
||
-- queries can be parameterized. The output of the parser phase will use @UnpreparedValue b@ for
|
||
-- the leaves, and most backends will then transform the AST to interpret those values and
|
||
-- consequently change @v@ to be @SQLExpression b@
|
||
--
|
||
-- r: BackendType -> Type
|
||
-- Joins across backends mean that the aforementioned @b@ parameter won't be the same throughout
|
||
-- the entire tree; at some point we will have an 'AnyBackend' used to encapsulate a branch that
|
||
-- uses a different @b@. We still want, however, to be able to parameterize the values of the
|
||
-- leaves in that separate branch, and that's what the @r@ parameter is for. We also use
|
||
-- 'UnpreparedValue' here during the parsing phase, meaning all leaf values will be
|
||
-- @UnpreparedValue b@ for their respective backend @b@, and most backends will then transform
|
||
-- their AST, cutting all such remote branches, and therefore using @Const Void@ for @r@.
|
||
module Hasura.RQL.IR.Select
|
||
( AggregateField (..),
|
||
AggregateFields,
|
||
AggregateOp (..),
|
||
AnnAggregateSelect,
|
||
AnnAggregateSelectG,
|
||
AnnColumnField (..),
|
||
AnnField,
|
||
AnnFieldG (..),
|
||
AnnFields,
|
||
AnnFieldsG,
|
||
AnnNestedObjectSelectG (..),
|
||
AnnNestedObjectSelect,
|
||
AnnNestedArraySelectG (..),
|
||
AnnNestedArraySelect,
|
||
AnnObjectSelect,
|
||
AnnObjectSelectG (..),
|
||
AnnSimpleSelect,
|
||
AnnSimpleSelectG,
|
||
AnnSimpleStreamSelect,
|
||
AnnSimpleStreamSelectG,
|
||
ArrayAggregateSelect,
|
||
ArrayAggregateSelectG,
|
||
ArrayConnectionSelect,
|
||
ArrayRelationSelectG,
|
||
ArraySelect,
|
||
ArraySelectFieldsG,
|
||
ArraySelectG (..),
|
||
SelectionField (..),
|
||
SelectionFields,
|
||
ComputedFieldScalarSelect (..),
|
||
ComputedFieldSelect (..),
|
||
ConnectionField (..),
|
||
ConnectionFields,
|
||
ConnectionSelect (..),
|
||
ConnectionSlice (..),
|
||
ConnectionSplit (..),
|
||
ConnectionSplitKind (..),
|
||
EdgeField (..),
|
||
EdgeFields,
|
||
ObjectRelationSelect,
|
||
ObjectRelationSelectG,
|
||
PageInfoField (..),
|
||
PageInfoFields,
|
||
QueryDB (..),
|
||
RemoteSourceSelect (..),
|
||
RemoteRelationshipSelect (..),
|
||
SourceRelationshipSelection (..),
|
||
TableAggregateField,
|
||
TableAggregateFieldG (..),
|
||
TableAggregateFields,
|
||
TableAggregateFieldsG,
|
||
CountDistinct (..),
|
||
insertFunctionArg,
|
||
mkAnnColumnField,
|
||
mkAnnColumnFieldAsText,
|
||
traverseSourceRelationshipSelection,
|
||
module Hasura.RQL.IR.Select.AnnSelectG,
|
||
module Hasura.RQL.IR.Select.Args,
|
||
module Hasura.RQL.IR.Select.From,
|
||
module Hasura.RQL.IR.Select.OrderBy,
|
||
module Hasura.RQL.IR.Select.TablePerm,
|
||
module Hasura.RQL.IR.Select.RelationSelect,
|
||
)
|
||
where
|
||
|
||
import Data.Bifoldable
|
||
import Data.HashMap.Strict qualified as HashMap
|
||
import Data.Kind (Type)
|
||
import Data.List.NonEmpty qualified as NE
|
||
import Data.Sequence qualified as Seq
|
||
import Hasura.Function.Cache
|
||
import Hasura.Prelude
|
||
import Hasura.RQL.IR.BoolExp
|
||
import Hasura.RQL.IR.OrderBy
|
||
import Hasura.RQL.IR.Select.AnnSelectG
|
||
import Hasura.RQL.IR.Select.Args
|
||
import Hasura.RQL.IR.Select.From
|
||
import Hasura.RQL.IR.Select.OrderBy
|
||
import Hasura.RQL.IR.Select.RelationSelect
|
||
import Hasura.RQL.IR.Select.TablePerm
|
||
import Hasura.RQL.Types.Backend
|
||
import Hasura.RQL.Types.BackendType
|
||
import Hasura.RQL.Types.Column
|
||
import Hasura.RQL.Types.Common
|
||
import Hasura.RQL.Types.ComputedField
|
||
import Hasura.RQL.Types.Instances ()
|
||
import Hasura.RQL.Types.Relationships.Remote
|
||
import Hasura.RQL.Types.Schema.Options (StringifyNumbers)
|
||
|
||
-- Root selection
|
||
|
||
data QueryDB (b :: BackendType) (r :: Type) v
|
||
= QDBMultipleRows (AnnSimpleSelectG b r v)
|
||
| QDBSingleRow (AnnSimpleSelectG b r v)
|
||
| QDBAggregation (AnnAggregateSelectG b r v)
|
||
| QDBConnection (ConnectionSelect b r v)
|
||
| QDBStreamMultipleRows (AnnSimpleStreamSelectG b r v)
|
||
deriving stock (Generic, Functor, Foldable, Traversable)
|
||
|
||
instance (Backend b) => Bifoldable (QueryDB b) where
|
||
bifoldMap f g = \case
|
||
QDBMultipleRows annSel -> bifoldMapAnnSelectG f g annSel
|
||
QDBSingleRow annSel -> bifoldMapAnnSelectG f g annSel
|
||
QDBAggregation annSel -> bifoldMapAnnSelectG f g annSel
|
||
QDBConnection connSel -> bifoldMap f g connSel
|
||
QDBStreamMultipleRows annSel -> bifoldMapAnnSelectStreamG f g annSel
|
||
|
||
-- Select
|
||
|
||
type AnnSimpleSelectG b r v = AnnSelectG b (AnnFieldG b r) v
|
||
|
||
type AnnAggregateSelectG b r v = AnnSelectG b (TableAggregateFieldG b r) v
|
||
|
||
type AnnSimpleStreamSelectG b r v = AnnSelectStreamG b (AnnFieldG b r) v
|
||
|
||
type AnnSimpleSelect b = AnnSimpleSelectG b Void (SQLExpression b)
|
||
|
||
type AnnAggregateSelect b = AnnAggregateSelectG b Void (SQLExpression b)
|
||
|
||
type AnnSimpleStreamSelect b = AnnSimpleStreamSelectG b Void (SQLExpression b)
|
||
|
||
-- Relay select
|
||
|
||
data ConnectionSelect (b :: BackendType) (r :: Type) v = ConnectionSelect
|
||
{ _csXRelay :: XRelay b,
|
||
_csPrimaryKeyColumns :: PrimaryKeyColumns b,
|
||
_csSplit :: Maybe (NE.NonEmpty (ConnectionSplit b v)),
|
||
_csSlice :: Maybe ConnectionSlice,
|
||
_csSelect :: (AnnSelectG b (ConnectionField b r) v)
|
||
}
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq (AnnSelectG b (ConnectionField b r) v),
|
||
Eq (ConnectionSlice),
|
||
Eq (ConnectionSplit b v),
|
||
Eq (PrimaryKeyColumns b)
|
||
) =>
|
||
Eq (ConnectionSelect b r v)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show (AnnSelectG b (ConnectionField b r) v),
|
||
Show (ConnectionSlice),
|
||
Show (ConnectionSplit b v),
|
||
Show (PrimaryKeyColumns b)
|
||
) =>
|
||
Show (ConnectionSelect b r v)
|
||
|
||
instance (Backend b) => Bifoldable (ConnectionSelect b) where
|
||
bifoldMap f g ConnectionSelect {..} =
|
||
foldMap (foldMap $ foldMap g) _csSplit
|
||
<> bifoldMapAnnSelectG f g _csSelect
|
||
|
||
data ConnectionSplit (b :: BackendType) v = ConnectionSplit
|
||
{ _csKind :: ConnectionSplitKind,
|
||
_csValue :: v,
|
||
_csOrderBy :: (OrderByItemG b (AnnotatedOrderByElement b v))
|
||
}
|
||
deriving stock (Functor, Generic, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq v,
|
||
Eq (OrderByItemG b (AnnotatedOrderByElement b v))
|
||
) =>
|
||
Eq (ConnectionSplit b v)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show v,
|
||
Show (OrderByItemG b (AnnotatedOrderByElement b v))
|
||
) =>
|
||
Show (ConnectionSplit b v)
|
||
|
||
instance
|
||
( Backend b,
|
||
Hashable v,
|
||
Hashable (OrderByItemG b (AnnotatedOrderByElement b v))
|
||
) =>
|
||
Hashable (ConnectionSplit b v)
|
||
|
||
data ConnectionSlice
|
||
= SliceFirst Int
|
||
| SliceLast Int
|
||
deriving stock (Show, Eq, Generic)
|
||
deriving anyclass (Hashable)
|
||
|
||
data ConnectionSplitKind
|
||
= CSKBefore
|
||
| CSKAfter
|
||
deriving stock (Show, Eq, Generic)
|
||
deriving anyclass (Hashable)
|
||
|
||
-- Fields
|
||
|
||
-- | captures a remote relationship's selection and the necessary context
|
||
data RemoteRelationshipSelect b r = RemoteRelationshipSelect
|
||
{ -- | The fields on the table that are required for the join condition
|
||
-- of the remote relationship
|
||
_rrsLHSJoinFields :: HashMap FieldName (DBJoinField b),
|
||
-- | The field that captures the relationship
|
||
-- r ~ (RemoteRelationshipField UnpreparedValue) when the AST is emitted by the parser.
|
||
-- r ~ Void when an execution tree is constructed so that a backend is
|
||
-- absolved of dealing with remote relationships.
|
||
_rrsRelationship :: r
|
||
}
|
||
deriving (Eq, Show, Functor, Foldable, Traversable)
|
||
|
||
data AnnFieldG (b :: BackendType) (r :: Type) v
|
||
= AFColumn (AnnColumnField b v)
|
||
| AFObjectRelation (ObjectRelationSelectG b r v)
|
||
| AFArrayRelation (ArraySelectG b r v)
|
||
| AFComputedField (XComputedField b) ComputedFieldName (ComputedFieldSelect b r v)
|
||
| -- | A remote relationship field
|
||
AFRemote (RemoteRelationshipSelect b r)
|
||
| AFNodeId (XRelay b) SourceName (TableName b) (PrimaryKeyColumns b)
|
||
| AFExpression Text
|
||
| -- | Nested object.
|
||
AFNestedObject (AnnNestedObjectSelectG b r v) -- TODO(dmoverton): move XNestedObject to a field in AFNestedObject constructor for consistency with AFNestedArray
|
||
| -- | Nested array
|
||
AFNestedArray (XNestedObjects b) (AnnNestedArraySelectG b r v)
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq (AnnColumnField b v),
|
||
Eq (ArraySelectG b r v),
|
||
Eq (ComputedFieldSelect b r v),
|
||
Eq (ObjectRelationSelectG b r v),
|
||
Eq (RemoteRelationshipSelect b r),
|
||
Eq (AnnNestedObjectSelectG b r v),
|
||
Eq (AnnNestedArraySelectG b r v)
|
||
) =>
|
||
Eq (AnnFieldG b r v)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show (AnnColumnField b v),
|
||
Show (ArraySelectG b r v),
|
||
Show (ComputedFieldSelect b r v),
|
||
Show (ObjectRelationSelectG b r v),
|
||
Show (RemoteRelationshipSelect b r),
|
||
Show (AnnNestedObjectSelectG b r v),
|
||
Show (AnnNestedArraySelectG b r v)
|
||
) =>
|
||
Show (AnnFieldG b r v)
|
||
|
||
instance (Backend b) => Bifoldable (AnnFieldG b) where
|
||
bifoldMap f g = \case
|
||
AFColumn col -> foldMap g col
|
||
AFObjectRelation objRel -> foldMap (bifoldMap f g) objRel
|
||
AFArrayRelation arrRel -> bifoldMap f g arrRel
|
||
AFComputedField _ _ cf -> bifoldMap f g cf
|
||
AFRemote r -> foldMap f r
|
||
AFNodeId {} -> mempty
|
||
AFExpression {} -> mempty
|
||
AFNestedObject no -> bifoldMap f g no
|
||
AFNestedArray _ na -> bifoldMap f g na
|
||
|
||
type AnnField b = AnnFieldG b Void (SQLExpression b)
|
||
|
||
type AnnFields b = AnnFieldsG b Void (SQLExpression b)
|
||
|
||
mkAnnColumnField ::
|
||
Column backend ->
|
||
ColumnType backend ->
|
||
Maybe (AnnColumnCaseBoolExp backend v) ->
|
||
Maybe (ScalarSelectionArguments backend) ->
|
||
AnnFieldG backend r v
|
||
mkAnnColumnField col typ caseBoolExp colOpM =
|
||
AFColumn (AnnColumnField col typ False colOpM caseBoolExp)
|
||
|
||
mkAnnColumnFieldAsText ::
|
||
ColumnInfo backend ->
|
||
AnnFieldG backend r v
|
||
mkAnnColumnFieldAsText ci =
|
||
AFColumn (AnnColumnField (ciColumn ci) (ciType ci) True Nothing Nothing)
|
||
|
||
traverseSourceRelationshipSelection ::
|
||
(Applicative f, Backend backend) =>
|
||
(vf backend -> f (vg backend)) ->
|
||
SourceRelationshipSelection backend r vf ->
|
||
f (SourceRelationshipSelection backend r vg)
|
||
traverseSourceRelationshipSelection f = \case
|
||
SourceRelationshipObject s ->
|
||
SourceRelationshipObject <$> traverse f s
|
||
SourceRelationshipArray s ->
|
||
SourceRelationshipArray <$> traverse f s
|
||
SourceRelationshipArrayAggregate s ->
|
||
SourceRelationshipArrayAggregate <$> traverse f s
|
||
|
||
-- Aggregation fields
|
||
|
||
data TableAggregateFieldG (b :: BackendType) (r :: Type) v
|
||
= TAFAgg (AggregateFields b v)
|
||
| TAFNodes (XNodesAgg b) (AnnFieldsG b r v)
|
||
| TAFExp Text
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq (AggregateFields b v),
|
||
Eq (AnnFieldsG b r v)
|
||
) =>
|
||
Eq (TableAggregateFieldG b r v)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show (AggregateFields b v),
|
||
Show (AnnFieldsG b r v)
|
||
) =>
|
||
Show (TableAggregateFieldG b r v)
|
||
|
||
instance (Backend b) => Bifoldable (TableAggregateFieldG b) where
|
||
bifoldMap f g = \case
|
||
TAFAgg {} -> mempty
|
||
TAFNodes _ fields -> foldMap (foldMap $ bifoldMap f g) fields
|
||
TAFExp {} -> mempty
|
||
|
||
data AggregateField (b :: BackendType) v
|
||
= AFCount (CountType b)
|
||
| AFOp (AggregateOp b v)
|
||
| AFExp Text
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
(Backend b, Eq (FunctionArgumentExp b v), Eq v) =>
|
||
Eq (AggregateField b v)
|
||
|
||
deriving stock instance
|
||
(Backend b, Show (FunctionArgumentExp b v), Show v) =>
|
||
Show (AggregateField b v)
|
||
|
||
data AggregateOp (b :: BackendType) v = AggregateOp
|
||
{ _aoOp :: Text,
|
||
_aoFields :: SelectionFields b v
|
||
}
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
(Backend b, Eq (FunctionArgumentExp b v), Eq v) =>
|
||
Eq (AggregateOp b v)
|
||
|
||
deriving stock instance
|
||
(Backend b, Show (FunctionArgumentExp b v), Show v) =>
|
||
Show (AggregateOp b v)
|
||
|
||
-- | Types of fields that can be selected in a user query.
|
||
data SelectionField (b :: BackendType) v
|
||
= SFCol (Column b) (ColumnType b)
|
||
| SFComputedField ComputedFieldName (ComputedFieldScalarSelect b v)
|
||
| SFExp Text
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
(Backend b, Eq (FunctionArgumentExp b v), Eq v) =>
|
||
Eq (SelectionField b v)
|
||
|
||
deriving stock instance
|
||
(Backend b, Show (FunctionArgumentExp b v), Show v) =>
|
||
Show (SelectionField b v)
|
||
|
||
type TableAggregateField b = TableAggregateFieldG b Void (SQLExpression b)
|
||
|
||
type TableAggregateFields b = TableAggregateFieldsG b Void (SQLExpression b)
|
||
|
||
type TableAggregateFieldsG b r v = Fields (TableAggregateFieldG b r v)
|
||
|
||
type SelectionFields b v = Fields (SelectionField b v)
|
||
|
||
type AggregateFields b v = Fields (AggregateField b v)
|
||
|
||
type AnnFieldsG b r v = Fields (AnnFieldG b r v)
|
||
|
||
-- Relay fields
|
||
|
||
data ConnectionField (b :: BackendType) (r :: Type) v
|
||
= ConnectionTypename Text
|
||
| ConnectionPageInfo PageInfoFields
|
||
| ConnectionEdges (EdgeFields b r v)
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Eq (EdgeFields b r v)
|
||
) =>
|
||
Eq (ConnectionField b r v)
|
||
|
||
deriving stock instance
|
||
( Show (EdgeFields b r v)
|
||
) =>
|
||
Show (ConnectionField b r v)
|
||
|
||
instance (Backend b) => Bifoldable (ConnectionField b) where
|
||
bifoldMap f g = \case
|
||
ConnectionTypename {} -> mempty
|
||
ConnectionPageInfo {} -> mempty
|
||
ConnectionEdges edgeFields -> foldMap (foldMap $ bifoldMap f g) edgeFields
|
||
|
||
data PageInfoField
|
||
= PageInfoTypename Text
|
||
| PageInfoHasNextPage
|
||
| PageInfoHasPreviousPage
|
||
| PageInfoStartCursor
|
||
| PageInfoEndCursor
|
||
deriving stock (Show, Eq)
|
||
|
||
data EdgeField (b :: BackendType) (r :: Type) v
|
||
= EdgeTypename Text
|
||
| EdgeCursor
|
||
| EdgeNode (AnnFieldsG b r v)
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Eq (AnnFieldsG b r v)
|
||
) =>
|
||
Eq (EdgeField b r v)
|
||
|
||
deriving stock instance
|
||
( Show (AnnFieldsG b r v)
|
||
) =>
|
||
Show (EdgeField b r v)
|
||
|
||
instance (Backend b) => Bifoldable (EdgeField b) where
|
||
bifoldMap f g = \case
|
||
EdgeTypename {} -> mempty
|
||
EdgeCursor -> mempty
|
||
EdgeNode annFields -> foldMap (foldMap $ bifoldMap f g) annFields
|
||
|
||
type ConnectionFields b r v = Fields (ConnectionField b r v)
|
||
|
||
type PageInfoFields = Fields PageInfoField
|
||
|
||
type EdgeFields b r v = Fields (EdgeField b r v)
|
||
|
||
data AnnColumnField (b :: BackendType) v = AnnColumnField
|
||
{ _acfColumn :: Column b,
|
||
_acfType :: ColumnType b,
|
||
-- | If this field is 'True', columns are explicitly casted to @text@ when
|
||
-- fetched, which avoids an issue that occurs because we don’t currently
|
||
-- have proper support for array types. See
|
||
-- https://github.com/hasura/graphql-engine/pull/3198 for more details.
|
||
_acfAsText :: Bool,
|
||
-- | Arguments of this column's selection. See 'ScalarSelectionArguments'
|
||
_acfArguments :: Maybe (ScalarSelectionArguments b),
|
||
-- | This type is used to determine whether the column
|
||
-- should be nullified. When the value is `Nothing`, the column value
|
||
-- will be outputted as computed and when the value is `Just c`, the
|
||
-- column will be outputted when `c` evaluates to `true` and `null`
|
||
-- when `c` evaluates to `false`.
|
||
_acfCaseBoolExpression :: (Maybe (AnnColumnCaseBoolExp b v))
|
||
}
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq (AnnColumnCaseBoolExp b v)
|
||
) =>
|
||
Eq (AnnColumnField b v)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show (AnnColumnCaseBoolExp b v)
|
||
) =>
|
||
Show (AnnColumnField b v)
|
||
|
||
-- Computed field
|
||
|
||
data ComputedFieldScalarSelect (b :: BackendType) v = ComputedFieldScalarSelect
|
||
{ _cfssFunction :: FunctionName b,
|
||
_cfssArguments :: FunctionArgsExp b v,
|
||
_cfssType :: ScalarType b,
|
||
_cfssScalarArguments :: (Maybe (ScalarSelectionArguments b))
|
||
}
|
||
|
||
deriving stock instance (Backend b) => Functor (ComputedFieldScalarSelect b)
|
||
|
||
deriving stock instance (Backend b) => Foldable (ComputedFieldScalarSelect b)
|
||
|
||
deriving stock instance (Backend b) => Traversable (ComputedFieldScalarSelect b)
|
||
|
||
deriving stock instance (Backend b, Show v, Show (FunctionArgumentExp b v)) => Show (ComputedFieldScalarSelect b v)
|
||
|
||
deriving stock instance (Backend b, Eq v, Eq (FunctionArgumentExp b v)) => Eq (ComputedFieldScalarSelect b v)
|
||
|
||
data ComputedFieldSelect (b :: BackendType) (r :: Type) v
|
||
= CFSScalar
|
||
-- | Type containing info about the computed field
|
||
(ComputedFieldScalarSelect b v)
|
||
-- | This type is used to determine if whether the scalar
|
||
-- computed field should be nullified. When the value is `Nothing`,
|
||
-- the scalar computed value will be outputted as computed and when the
|
||
-- value is `Just c`, the scalar computed field will be outputted when
|
||
-- `c` evaluates to `true` and `null` when `c` evaluates to `false`
|
||
(Maybe (AnnColumnCaseBoolExp b v))
|
||
| CFSTable JsonAggSelect (AnnSimpleSelectG b r v)
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq (AnnColumnCaseBoolExp b v),
|
||
Eq (AnnSimpleSelectG b r v),
|
||
Eq (ComputedFieldScalarSelect b v)
|
||
) =>
|
||
Eq (ComputedFieldSelect b r v)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show (AnnColumnCaseBoolExp b v),
|
||
Show (AnnSimpleSelectG b r v),
|
||
Show (ComputedFieldScalarSelect b v)
|
||
) =>
|
||
Show (ComputedFieldSelect b r v)
|
||
|
||
instance (Backend b) => Bifoldable (ComputedFieldSelect b) where
|
||
bifoldMap f g = \case
|
||
CFSScalar cfsSelect caseBoolExp -> foldMap g cfsSelect <> foldMap (foldMap $ foldMap g) caseBoolExp
|
||
CFSTable _ simpleSelect -> bifoldMapAnnSelectG f g simpleSelect
|
||
|
||
-- Local relationship
|
||
|
||
type ArrayRelationSelectG b r v = AnnRelationSelectG b (AnnSimpleSelectG b r v)
|
||
|
||
type ArrayAggregateSelectG b r v = AnnRelationSelectG b (AnnAggregateSelectG b r v)
|
||
|
||
type ArrayConnectionSelect b r v = AnnRelationSelectG b (ConnectionSelect b r v)
|
||
|
||
type ArrayAggregateSelect b = ArrayAggregateSelectG b Void (SQLExpression b)
|
||
|
||
data AnnObjectSelectG (b :: BackendType) (r :: Type) v = AnnObjectSelectG
|
||
{ _aosFields :: AnnFieldsG b r v,
|
||
_aosTarget :: SelectFromG b v,
|
||
_aosTargetFilter :: (AnnBoolExp b v)
|
||
}
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq (SelectFromG b v),
|
||
Eq (AnnBoolExp b v),
|
||
Eq (AnnFieldsG b r v)
|
||
) =>
|
||
Eq (AnnObjectSelectG b r v)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show (SelectFromG b v),
|
||
Show (AnnBoolExp b v),
|
||
Show (AnnFieldsG b r v)
|
||
) =>
|
||
Show (AnnObjectSelectG b r v)
|
||
|
||
instance (Backend b) => Bifoldable (AnnObjectSelectG b) where
|
||
bifoldMap f g AnnObjectSelectG {..} =
|
||
foldMap (foldMap $ bifoldMap f g) _aosFields <> foldMap (foldMap g) _aosTargetFilter
|
||
|
||
type AnnObjectSelect b r = AnnObjectSelectG b r (SQLExpression b)
|
||
|
||
type ObjectRelationSelectG b r v = AnnRelationSelectG b (AnnObjectSelectG b r v)
|
||
|
||
type ObjectRelationSelect b = ObjectRelationSelectG b Void (SQLExpression b)
|
||
|
||
data ArraySelectG (b :: BackendType) (r :: Type) v
|
||
= ASSimple (ArrayRelationSelectG b r v)
|
||
| ASAggregate (ArrayAggregateSelectG b r v)
|
||
| ASConnection (ArrayConnectionSelect b r v)
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Eq (ArrayRelationSelectG b r v),
|
||
Eq (ArrayAggregateSelectG b r v),
|
||
Eq (ArrayConnectionSelect b r v)
|
||
) =>
|
||
Eq (ArraySelectG b r v)
|
||
|
||
deriving stock instance
|
||
( Show (ArrayRelationSelectG b r v),
|
||
Show (ArrayAggregateSelectG b r v),
|
||
Show (ArrayConnectionSelect b r v)
|
||
) =>
|
||
Show (ArraySelectG b r v)
|
||
|
||
instance (Backend b) => Bifoldable (ArraySelectG b) where
|
||
bifoldMap f g = \case
|
||
ASSimple arrayRelationSelect -> foldMap (bifoldMapAnnSelectG f g) arrayRelationSelect
|
||
ASAggregate arrayAggregateSelect -> foldMap (bifoldMapAnnSelectG f g) arrayAggregateSelect
|
||
ASConnection arrayConnectionSelect -> foldMap (bifoldMap f g) arrayConnectionSelect
|
||
|
||
type ArraySelect b = ArraySelectG b Void (SQLExpression b)
|
||
|
||
type ArraySelectFieldsG b r v = Fields (ArraySelectG b r v)
|
||
|
||
-- | Captures the selection set of a remote source relationship.
|
||
data
|
||
SourceRelationshipSelection
|
||
(b :: BackendType)
|
||
(r :: Type)
|
||
(vf :: BackendType -> Type)
|
||
= SourceRelationshipObject (AnnObjectSelectG b r (vf b))
|
||
| SourceRelationshipArray (AnnSimpleSelectG b r (vf b))
|
||
| SourceRelationshipArrayAggregate (AnnAggregateSelectG b r (vf b))
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq (AnnAggregateSelectG b r (vf b)),
|
||
Eq (AnnObjectSelectG b r (vf b)),
|
||
Eq (AnnSimpleSelectG b r (vf b))
|
||
) =>
|
||
Eq (SourceRelationshipSelection b r vf)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show (AnnAggregateSelectG b r (vf b)),
|
||
Show (AnnObjectSelectG b r (vf b)),
|
||
Show (AnnSimpleSelectG b r (vf b))
|
||
) =>
|
||
Show (SourceRelationshipSelection b r vf)
|
||
|
||
-- | A relationship to a remote source. 'vf' (could use a better name) is
|
||
-- analogous to 'v' in other IR types such as 'AnnFieldG'. vf's kind is
|
||
-- (BackendType -> Type) instead of v's 'Type' so that 'v' of 'AnnFieldG' can
|
||
-- be specific to the backend that it captures ('b' of an AnnFieldG changes as
|
||
-- we walk down the IR branches which capture relationships to other databases)
|
||
data
|
||
RemoteSourceSelect
|
||
(r :: Type)
|
||
(vf :: BackendType -> Type)
|
||
(tgt :: BackendType) = RemoteSourceSelect
|
||
{ _rssName :: SourceName,
|
||
_rssConfig :: SourceConfig tgt,
|
||
_rssSelection :: SourceRelationshipSelection tgt r vf,
|
||
-- | Additional information about the source's join columns:
|
||
-- (ScalarType tgt) so that the remote can interpret the join values coming
|
||
-- from src
|
||
-- (Column tgt) so that an appropriate join condition / IN clause can be built
|
||
-- by the remote
|
||
_rssJoinMapping :: (HashMap.HashMap FieldName (ScalarType tgt, Column tgt)),
|
||
_rssStringifyNums :: StringifyNumbers
|
||
}
|
||
|
||
deriving stock instance
|
||
( Backend tgt,
|
||
Eq (SourceRelationshipSelection tgt r vf)
|
||
) =>
|
||
Eq (RemoteSourceSelect r vf tgt)
|
||
|
||
deriving stock instance
|
||
( Backend tgt,
|
||
Show (SourceRelationshipSelection tgt r vf),
|
||
Show (SourceConfig tgt)
|
||
) =>
|
||
Show (RemoteSourceSelect r vf tgt)
|
||
|
||
-- Nested objects
|
||
|
||
data AnnNestedObjectSelectG (b :: BackendType) (r :: Type) v = AnnNestedObjectSelectG
|
||
{ _anosSupportsNestedObjects :: XNestedObjects b,
|
||
_anosColumn :: Column b,
|
||
_anosFields :: AnnFieldsG b r v
|
||
}
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Eq (AnnFieldsG b r v)
|
||
) =>
|
||
Eq (AnnNestedObjectSelectG b r v)
|
||
|
||
deriving stock instance
|
||
( Backend b,
|
||
Show (AnnFieldsG b r v)
|
||
) =>
|
||
Show (AnnNestedObjectSelectG b r v)
|
||
|
||
instance (Backend b) => Bifoldable (AnnNestedObjectSelectG b) where
|
||
bifoldMap f g AnnNestedObjectSelectG {..} =
|
||
foldMap (foldMap $ bifoldMap f g) _anosFields
|
||
|
||
type AnnNestedObjectSelect b r = AnnNestedObjectSelectG b r (SQLExpression b)
|
||
|
||
-- Nested arrays
|
||
|
||
data AnnNestedArraySelectG (b :: BackendType) (r :: Type) v
|
||
= ANASSimple (AnnFieldG b r v)
|
||
| ANASAggregate (AnnAggregateSelectG b r v)
|
||
deriving stock (Functor, Foldable, Traversable)
|
||
|
||
deriving stock instance
|
||
(Backend b, Eq (AnnFieldG b r v), Eq (AnnAggregateSelectG b r v)) => Eq (AnnNestedArraySelectG b r v)
|
||
|
||
deriving stock instance
|
||
(Backend b, Show (AnnFieldG b r v), Show (AnnAggregateSelectG b r v)) => Show (AnnNestedArraySelectG b r v)
|
||
|
||
instance (Backend b) => Bifoldable (AnnNestedArraySelectG b) where
|
||
bifoldMap f g = \case
|
||
ANASSimple field -> bifoldMap f g field
|
||
ANASAggregate agg -> bifoldMapAnnSelectG f g agg
|
||
|
||
type AnnNestedArraySelect b r = AnnNestedArraySelectG b r (SQLExpression b)
|
||
|
||
-- | If argument positional index is less than or equal to length of
|
||
-- 'positional' arguments then insert the value in 'positional' arguments else
|
||
-- insert the value with argument name in 'named' arguments
|
||
insertFunctionArg ::
|
||
FunctionArgName ->
|
||
Int ->
|
||
a ->
|
||
FunctionArgsExpG a ->
|
||
FunctionArgsExpG a
|
||
insertFunctionArg argName idx value (FunctionArgsExp positional named) =
|
||
if (idx + 1) <= length positional
|
||
then FunctionArgsExp (insertAt idx value positional) named
|
||
else
|
||
FunctionArgsExp positional
|
||
$ HashMap.insert (getFuncArgNameTxt argName) value named
|
||
where
|
||
insertAt i a = toList . Seq.insertAt i a . Seq.fromList
|
||
|
||
-- | The "distinct" input field inside "count" aggregate field
|
||
--
|
||
-- count (
|
||
-- distinct: Boolean
|
||
-- ): Int
|
||
data CountDistinct
|
||
= SelectCountDistinct
|
||
| SelectCountNonDistinct
|