mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 20:41:49 +03:00
4b0f5f09c9
GJ IR changes cherry-picked from the original GJ branch. There is a separate (can be merged independently) PR for metadata changes (#1727) and there will be a different PR upcoming PR for execution changes. https://github.com/hasura/graphql-engine-mono/pull/1810 Co-authored-by: Vamshi Surabhi <6562944+0x777@users.noreply.github.com> GitOrigin-RevId: c31956af29dc9c9b75d002aba7d93c230697c5f4
492 lines
18 KiB
Haskell
492 lines
18 KiB
Haskell
{-# 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 where
|
||
|
||
import Hasura.Prelude
|
||
|
||
import qualified Data.HashMap.Strict as HM
|
||
import qualified Data.List.NonEmpty as NE
|
||
import qualified Data.Sequence as Seq
|
||
import qualified Language.GraphQL.Draft.Syntax as G
|
||
|
||
import Control.Lens.TH (makeLenses, makePrisms)
|
||
import Data.Int (Int64)
|
||
import Data.Kind (Type)
|
||
|
||
import qualified Hasura.SQL.AnyBackend as AB
|
||
|
||
import Hasura.GraphQL.Parser.Schema (InputValue)
|
||
import Hasura.RQL.IR.BoolExp
|
||
import Hasura.RQL.IR.OrderBy
|
||
import Hasura.RQL.Types.Backend
|
||
import Hasura.RQL.Types.Column
|
||
import Hasura.RQL.Types.Common
|
||
import Hasura.RQL.Types.ComputedField
|
||
import Hasura.RQL.Types.Function
|
||
import Hasura.RQL.Types.Instances ()
|
||
import Hasura.RQL.Types.Relationship
|
||
import Hasura.RQL.Types.RemoteRelationship
|
||
import Hasura.RQL.Types.RemoteSchema
|
||
import Hasura.SQL.Backend
|
||
|
||
|
||
-- Root selection
|
||
|
||
data QueryDB (b :: BackendType) (r :: BackendType -> Type) v
|
||
= QDBMultipleRows (AnnSimpleSelectG b r v)
|
||
| QDBSingleRow (AnnSimpleSelectG b r v)
|
||
| QDBAggregation (AnnAggregateSelectG b r v)
|
||
| QDBConnection (ConnectionSelect b r v)
|
||
deriving stock (Generic, Functor, Foldable, Traversable)
|
||
|
||
|
||
-- Select
|
||
|
||
data AnnSelectG (b :: BackendType) (r :: BackendType -> Type) (f :: Type -> Type) (v :: Type)
|
||
= AnnSelectG
|
||
{ _asnFields :: !(Fields (f v))
|
||
, _asnFrom :: !(SelectFromG b v)
|
||
, _asnPerm :: !(TablePermG b v)
|
||
, _asnArgs :: !(SelectArgsG b v)
|
||
, _asnStrfyNum :: !Bool
|
||
} deriving (Functor, Foldable, Traversable)
|
||
|
||
type AnnSimpleSelectG b r v = AnnSelectG b r (AnnFieldG b r) v
|
||
type AnnAggregateSelectG b r v = AnnSelectG b r (TableAggregateFieldG b r) v
|
||
type AnnSimpleSelect b = AnnSimpleSelectG b (Const Void) (SQLExpression b)
|
||
type AnnAggregateSelect b = AnnAggregateSelectG b (Const Void) (SQLExpression b)
|
||
|
||
|
||
-- Relay select
|
||
|
||
data ConnectionSelect (b :: BackendType) (r :: BackendType -> Type) v
|
||
= ConnectionSelect
|
||
{ _csXRelay :: !(XRelay b)
|
||
, _csPrimaryKeyColumns :: !(PrimaryKeyColumns b)
|
||
, _csSplit :: !(Maybe (NE.NonEmpty (ConnectionSplit b v)))
|
||
, _csSlice :: !(Maybe ConnectionSlice)
|
||
, _csSelect :: !(AnnSelectG b r (ConnectionField b r) v)
|
||
} deriving (Functor, Foldable, Traversable)
|
||
|
||
data ConnectionSplit (b :: BackendType) v
|
||
= ConnectionSplit
|
||
{ _csKind :: !ConnectionSplitKind
|
||
, _csValue :: !v
|
||
, _csOrderBy :: !(OrderByItemG b (AnnOrderByElementG b ()))
|
||
} deriving (Functor, Generic, Foldable, Traversable)
|
||
instance (Backend b, Hashable (ColumnInfo b), Hashable v) => Hashable (ConnectionSplit b v)
|
||
|
||
data ConnectionSlice
|
||
= SliceFirst !Int
|
||
| SliceLast !Int
|
||
deriving (Show, Eq, Generic)
|
||
instance Hashable ConnectionSlice
|
||
|
||
data ConnectionSplitKind
|
||
= CSKBefore
|
||
| CSKAfter
|
||
deriving (Show, Eq, Generic)
|
||
instance Hashable ConnectionSplitKind
|
||
|
||
|
||
-- From
|
||
|
||
data SelectFromG (b :: BackendType) v
|
||
= FromTable !(TableName b)
|
||
| FromIdentifier !(Identifier b)
|
||
| FromFunction !(FunctionName b)
|
||
!(FunctionArgsExpTableRow b v)
|
||
-- a definition list
|
||
!(Maybe [(Column b, ScalarType b)])
|
||
deriving (Functor, Foldable, Traversable, Generic)
|
||
instance (Backend b, Hashable v) => Hashable (SelectFromG b v)
|
||
|
||
type SelectFrom b = SelectFromG b (SQLExpression b)
|
||
|
||
|
||
-- Select arguments
|
||
|
||
data SelectArgsG (b :: BackendType) v
|
||
= SelectArgs
|
||
{ _saWhere :: !(Maybe (AnnBoolExp b v))
|
||
, _saOrderBy :: !(Maybe (NE.NonEmpty (AnnOrderByItemG b v)))
|
||
, _saLimit :: !(Maybe Int)
|
||
, _saOffset :: !(Maybe Int64)
|
||
, _saDistinct :: !(Maybe (NE.NonEmpty (Column b)))
|
||
} deriving (Generic, Functor, Foldable, Traversable)
|
||
|
||
deriving instance
|
||
( Backend b
|
||
, Eq (BooleanOperators b v)
|
||
, Eq v
|
||
) => Eq (SelectArgsG b v)
|
||
|
||
instance
|
||
( Backend b
|
||
, Hashable (BooleanOperators b v)
|
||
, Hashable v
|
||
) => Hashable (SelectArgsG b v)
|
||
|
||
type SelectArgs b = SelectArgsG b (SQLExpression b)
|
||
|
||
noSelectArgs :: SelectArgsG backend v
|
||
noSelectArgs = SelectArgs Nothing Nothing Nothing Nothing Nothing
|
||
|
||
|
||
-- Order by argument
|
||
|
||
data AnnOrderByElementG (b :: BackendType) v
|
||
= AOCColumn !(ColumnInfo b)
|
||
| AOCObjectRelation !(RelInfo b) !v !(AnnOrderByElementG b v)
|
||
| AOCArrayAggregation !(RelInfo b) !v !(AnnAggregateOrderBy b)
|
||
deriving (Generic, Functor, Foldable, Traversable)
|
||
deriving instance (Backend b, Eq v) => Eq (AnnOrderByElementG b v)
|
||
instance (Backend b, Hashable v) => Hashable (AnnOrderByElementG b v)
|
||
|
||
data AnnAggregateOrderBy (b :: BackendType)
|
||
= AAOCount
|
||
| AAOOp !Text !(ColumnInfo b)
|
||
deriving (Generic)
|
||
deriving instance (Backend b) => Eq (AnnAggregateOrderBy b)
|
||
instance (Backend b) => Hashable (AnnAggregateOrderBy b)
|
||
|
||
type AnnOrderByElement b v = AnnOrderByElementG b (AnnBoolExp b v)
|
||
type AnnOrderByItemG b v = OrderByItemG b (AnnOrderByElement b v)
|
||
type AnnOrderByItem b = AnnOrderByItemG b (SQLExpression b)
|
||
|
||
|
||
-- Fields
|
||
|
||
type Fields a = [(FieldName, a)]
|
||
|
||
data AnnFieldG (b :: BackendType) (r :: BackendType -> 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 relationship to a remote source/remote schema. Its kind is
|
||
-- (r :: BackendType -> Type) so that AFRemote can capture something
|
||
-- that is specific to the backend AnnFieldG. See RemoteSelect. When
|
||
-- remote joins are extracted from the structure, 'r' becomes 'Const Void'
|
||
| AFRemote !(r b)
|
||
| AFNodeId !(XRelay b) !(TableName b) !(PrimaryKeyColumns b)
|
||
| AFExpression !Text
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
type AnnField b = AnnFieldG b (Const Void) (SQLExpression b)
|
||
type AnnFields b = AnnFieldsG b (Const Void) (SQLExpression b)
|
||
|
||
mkAnnColumnField
|
||
:: ColumnInfo backend
|
||
-> Maybe (AnnColumnCaseBoolExp backend v)
|
||
-> Maybe (ColumnOp backend)
|
||
-> AnnFieldG backend r v
|
||
mkAnnColumnField ci caseBoolExp colOpM =
|
||
AFColumn (AnnColumnField ci False colOpM caseBoolExp)
|
||
|
||
mkAnnColumnFieldAsText
|
||
:: ColumnInfo backend
|
||
-> AnnFieldG backend r v
|
||
mkAnnColumnFieldAsText ci =
|
||
AFColumn (AnnColumnField ci True Nothing Nothing)
|
||
|
||
-- Aggregation fields
|
||
|
||
data TableAggregateFieldG (b :: BackendType) (r :: BackendType -> Type) v
|
||
= TAFAgg !(AggregateFields b)
|
||
| TAFNodes (XNodesAgg b) !(AnnFieldsG b r v)
|
||
| TAFExp !Text
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
data AggregateField (b :: BackendType)
|
||
= AFCount !(CountType b)
|
||
| AFOp !(AggregateOp b)
|
||
| AFExp !Text
|
||
|
||
data AggregateOp (b :: BackendType)
|
||
= AggregateOp
|
||
{ _aoOp :: !Text
|
||
, _aoFields :: !(ColumnFields b)
|
||
}
|
||
|
||
data ColFld (b :: BackendType)
|
||
= CFCol !(Column b) !(ColumnType b)
|
||
| CFExp !Text
|
||
|
||
type TableAggregateField b = TableAggregateFieldG b (Const Void) (SQLExpression b)
|
||
type TableAggregateFields b = TableAggregateFieldsG b (Const Void) (SQLExpression b)
|
||
type TableAggregateFieldsG b r v = Fields (TableAggregateFieldG b r v)
|
||
|
||
type ColumnFields b = Fields (ColFld b)
|
||
type AggregateFields b = Fields (AggregateField b)
|
||
type AnnFieldsG b r v = Fields (AnnFieldG b r v)
|
||
|
||
|
||
-- Relay fields
|
||
|
||
data ConnectionField (b :: BackendType) (r :: BackendType -> Type) v
|
||
= ConnectionTypename !Text
|
||
| ConnectionPageInfo !PageInfoFields
|
||
| ConnectionEdges !(EdgeFields b r v)
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
data PageInfoField
|
||
= PageInfoTypename !Text
|
||
| PageInfoHasNextPage
|
||
| PageInfoHasPreviousPage
|
||
| PageInfoStartCursor
|
||
| PageInfoEndCursor
|
||
deriving (Show, Eq)
|
||
|
||
data EdgeField (b :: BackendType) (r :: BackendType -> Type) v
|
||
= EdgeTypename !Text
|
||
| EdgeCursor
|
||
| EdgeNode !(AnnFieldsG b r v)
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
type ConnectionFields b r v = Fields (ConnectionField b r v)
|
||
|
||
type PageInfoFields = Fields PageInfoField
|
||
type EdgeFields b r v = Fields (EdgeField b r v)
|
||
|
||
-- Column
|
||
|
||
data AnnColumnField (b :: BackendType) v
|
||
= AnnColumnField
|
||
{ _acfInfo :: !(ColumnInfo b)
|
||
, _acfAsText :: !Bool
|
||
-- ^ 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.
|
||
, _acfOp :: !(Maybe (ColumnOp b))
|
||
, _acfCaseBoolExpression :: !(Maybe (AnnColumnCaseBoolExp b v))
|
||
-- ^ This type is used to determine if 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`.
|
||
} deriving (Functor, Foldable, Traversable)
|
||
|
||
data ColumnOp (b :: BackendType)
|
||
= ColumnOp
|
||
{ _colOp :: SQLOperator b
|
||
, _colExp :: SQLExpression b
|
||
}
|
||
|
||
deriving instance Backend b => Show (ColumnOp b)
|
||
deriving instance Backend b => Eq (ColumnOp b)
|
||
|
||
|
||
-- Computed field
|
||
|
||
data ComputedFieldScalarSelect (b :: BackendType) v
|
||
= ComputedFieldScalarSelect
|
||
{ _cfssFunction :: !(FunctionName b)
|
||
, _cfssArguments :: !(FunctionArgsExpTableRow b v)
|
||
, _cfssType :: !(ScalarType b)
|
||
, _cfssColumnOp :: !(Maybe (ColumnOp b))
|
||
} deriving (Functor, Foldable, Traversable)
|
||
deriving instance (Backend b, Show v) => Show (ComputedFieldScalarSelect b v)
|
||
deriving instance (Backend b, Eq v) => Eq (ComputedFieldScalarSelect b v)
|
||
|
||
data ComputedFieldSelect (b :: BackendType) (r :: BackendType -> Type) v
|
||
= CFSScalar
|
||
!(ComputedFieldScalarSelect b v)
|
||
-- ^ Type containing info about the computed field
|
||
!(Maybe (AnnColumnCaseBoolExp 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`
|
||
| CFSTable !JsonAggSelect !(AnnSimpleSelectG b r v)
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
|
||
-- Local relationship
|
||
|
||
data AnnRelationSelectG (b :: BackendType) a
|
||
= AnnRelationSelectG
|
||
{ aarRelationshipName :: !RelName -- Relationship name
|
||
, aarColumnMapping :: !(HashMap (Column b) (Column b)) -- Column of left table to join with
|
||
, aarAnnSelect :: !a -- Current table. Almost ~ to SQL Select
|
||
} deriving (Functor, Foldable, Traversable)
|
||
|
||
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 (Const Void) (SQLExpression b)
|
||
|
||
data AnnObjectSelectG (b :: BackendType) (r :: BackendType -> Type) v
|
||
= AnnObjectSelectG
|
||
{ _aosFields :: !(AnnFieldsG b r v)
|
||
, _aosTableFrom :: !(TableName b)
|
||
, _aosTableFilter :: !(AnnBoolExp b v)
|
||
} deriving (Functor, Foldable, Traversable)
|
||
|
||
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 (Const Void) (SQLExpression b)
|
||
|
||
|
||
data ArraySelectG (b :: BackendType) (r :: BackendType -> Type) v
|
||
= ASSimple !(ArrayRelationSelectG b r v)
|
||
| ASAggregate !(ArrayAggregateSelectG b r v)
|
||
| ASConnection !(ArrayConnectionSelect b r v)
|
||
deriving (Functor, Foldable, Traversable)
|
||
|
||
type ArraySelect b = ArraySelectG b (Const Void) (SQLExpression b)
|
||
type ArraySelectFieldsG b r v = Fields (ArraySelectG b r v)
|
||
|
||
|
||
-- Remote schema relationships
|
||
|
||
data RemoteFieldArgument
|
||
= RemoteFieldArgument
|
||
{ _rfaArgument :: !G.Name
|
||
, _rfaValue :: !(InputValue RemoteSchemaVariable)
|
||
} deriving (Eq,Show)
|
||
|
||
data RemoteSchemaSelect (b :: BackendType)
|
||
= RemoteSchemaSelect
|
||
{ _rselArgs :: ![RemoteFieldArgument]
|
||
, _rselSelection :: !(G.SelectionSet G.NoFragments RemoteSchemaVariable)
|
||
, _rselHasuraFields :: !(HashSet (DBJoinField b))
|
||
, _rselFieldCall :: !(NonEmpty FieldCall)
|
||
, _rselRemoteSchema :: !RemoteSchemaInfo
|
||
}
|
||
|
||
-- | Captures the selection set of a remote source relationship.
|
||
data SourceRelationshipSelection
|
||
(b :: BackendType)
|
||
(r :: BackendType -> Type)
|
||
(vf :: BackendType -> Type)
|
||
= SourceRelationshipObject !(AnnObjectSelectG b r (vf b))
|
||
| SourceRelationshipArray !(AnnSimpleSelectG b r (vf b))
|
||
| SourceRelationshipArrayAggregate !(AnnAggregateSelectG b r (vf b))
|
||
|
||
-- | 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
|
||
(src :: BackendType)
|
||
(vf :: BackendType -> Type)
|
||
(tgt :: BackendType)
|
||
= RemoteSourceSelect
|
||
{ _rssSourceName :: !SourceName
|
||
, _rssSourceConfig :: !(SourceConfig tgt)
|
||
, _rssSelection :: !(SourceRelationshipSelection tgt (RemoteSelect vf) vf)
|
||
, _rssJoinMapping :: !(HM.HashMap FieldName (ColumnInfo src, ScalarType tgt, Column tgt))
|
||
-- ^ Additional information about the source's join columns:
|
||
-- (ColumnInfo src) so that we can add the join column to the AST
|
||
-- (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
|
||
}
|
||
|
||
-- | A remote relationship to either a remote schema or a remote source.
|
||
-- See RemoteSourceSelect for explanation on 'vf'.
|
||
data RemoteSelect
|
||
(vf :: BackendType -> Type)
|
||
(src :: BackendType)
|
||
= RemoteSelectRemoteSchema !(RemoteSchemaSelect src)
|
||
-- ^ AnyBackend is used here to capture a relationship to an arbitrary target
|
||
| RemoteSelectSource !(AB.AnyBackend (RemoteSourceSelect src vf))
|
||
|
||
-- Permissions
|
||
|
||
data TablePermG (b :: BackendType) v
|
||
= TablePerm
|
||
{ _tpFilter :: !(AnnBoolExp b v)
|
||
, _tpLimit :: !(Maybe Int)
|
||
} deriving (Generic, Functor, Foldable, Traversable)
|
||
|
||
instance
|
||
( Backend b
|
||
, Hashable (BooleanOperators b v)
|
||
, Hashable (ColumnInfo b)
|
||
, Hashable v
|
||
) => Hashable (TablePermG b v)
|
||
|
||
type TablePerm b = TablePermG b (SQLExpression b)
|
||
|
||
noTablePermissions :: TablePermG backend v
|
||
noTablePermissions = TablePerm annBoolExpTrue Nothing
|
||
|
||
|
||
-- Function arguments
|
||
|
||
data ArgumentExp (b :: BackendType) a
|
||
= AETableRow !(Maybe (Identifier b)) -- ^ table row accessor
|
||
| AESession !a -- ^ JSON/JSONB hasura session variable object
|
||
| AEInput !a
|
||
deriving (Functor, Foldable, Traversable, Generic)
|
||
deriving instance (Backend b, Show a) => Show (ArgumentExp b a)
|
||
deriving instance (Backend b, Eq a) => Eq (ArgumentExp b a)
|
||
instance (Backend b, Hashable v) => Hashable (ArgumentExp b v)
|
||
|
||
data FunctionArgsExpG a
|
||
= FunctionArgsExp
|
||
{ _faePositional :: ![a]
|
||
, _faeNamed :: !(HM.HashMap Text a)
|
||
} deriving (Show, Eq, Functor, Foldable, Traversable, Generic)
|
||
instance (Hashable a) => Hashable (FunctionArgsExpG a)
|
||
|
||
type FunctionArgsExpTableRow b v = FunctionArgsExpG (ArgumentExp b v)
|
||
type FunctionArgExp b = FunctionArgsExpG (SQLExpression b)
|
||
|
||
emptyFunctionArgsExp :: FunctionArgsExpG a
|
||
emptyFunctionArgsExp = FunctionArgsExp [] HM.empty
|
||
|
||
-- | 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 $
|
||
HM.insert (getFuncArgNameTxt argName) value named
|
||
where
|
||
insertAt i a = toList . Seq.insertAt i a . Seq.fromList
|
||
|
||
|
||
-- Lenses
|
||
|
||
$(makeLenses ''AnnSelectG)
|
||
$(makePrisms ''AnnFieldG)
|
||
$(makePrisms ''AnnOrderByElementG)
|