mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 20:41:49 +03:00
7ca48decfb
> ### Description > This PR is an incremental work towards [enabling insert mutations on MSSQL](https://github.com/hasura/graphql-engine-mono/pull/1974). In this PR, we generate insert mutation schema parser for MSSQL backend. ### Changelog - [ ] `CHANGELOG.md` is updated with user-facing content relevant to this PR. If no changelog is required, then add the `no-changelog-required` label. ### Affected components - [x] Server https://github.com/hasura/graphql-engine-mono/pull/2141 GitOrigin-RevId: 8595008dece35f7fded9c52e134de8b97b64f53f
154 lines
5.9 KiB
Haskell
154 lines
5.9 KiB
Haskell
{- | Internal representation of an insertion in a database table.
|
|
|
|
What makes this specific mutation tricky is that we support recursive
|
|
insertions, across local relationships. Because local joins come in two
|
|
different kinds (object relationships and array relations), and because for each
|
|
table we expose two root different root fields (insert_one and insert), we
|
|
distinguish between *single row inserts* and *multi rows inserts*.
|
|
|
|
TODO: the distinction between single-row inserts and multi-rows inserts does not
|
|
need to be enforced the way it currently is, with booleans and different
|
|
types. The distinction could be made in the translation layer, if need be.
|
|
|
|
-}
|
|
|
|
module Hasura.RQL.IR.Insert where
|
|
|
|
import Hasura.Prelude
|
|
|
|
import Control.Lens ((^?))
|
|
import Control.Lens.TH (makePrisms)
|
|
import Data.Kind (Type)
|
|
|
|
import Hasura.RQL.IR.BoolExp
|
|
import Hasura.RQL.IR.Returning
|
|
import Hasura.RQL.Types.Backend
|
|
import Hasura.RQL.Types.Column
|
|
import Hasura.RQL.Types.Relationship
|
|
import Hasura.SQL.Backend
|
|
|
|
|
|
-- | Overall representation of an insert mutation, corresponding to one root
|
|
-- field in our mutation, including the parsed selection set of the mutation's
|
|
-- output. For historical reasons, it will always contain a `MultiObjIns`,
|
|
-- whether the root mutation is a single row or not, and will distinguish
|
|
-- between them using a boolean field.
|
|
|
|
data AnnInsert (b :: BackendType) (r :: BackendType -> Type) v
|
|
= AnnInsert
|
|
{ _aiFieldName :: !Text
|
|
, _aiIsSingle :: !Bool
|
|
, _aiData :: !(MultiObjIns b v)
|
|
, _aiOutput :: !(MutationOutputG b r v)
|
|
} deriving (Functor, Foldable, Traversable)
|
|
|
|
|
|
-- | One individual insert, one node from the tree.
|
|
-- The @f@ parameter is used to construct the container for the values to be
|
|
-- inserted: 'Single' for a single-row insert, '[]' for a multi-row insert.
|
|
|
|
data AnnIns (b :: BackendType) (f :: Type -> Type) (v :: Type)
|
|
= AnnIns
|
|
{ _aiInsObj :: !(f (AnnotatedInsertRow b v))
|
|
, _aiTableName :: !(TableName b)
|
|
, _aiConflictClause :: !(Maybe (ConflictClauseP1 b v))
|
|
, _aiCheckCond :: !(AnnBoolExp b v, Maybe (AnnBoolExp b v))
|
|
, _aiTableCols :: ![ColumnInfo b]
|
|
, _aiDefVals :: !(PreSetColsG b v)
|
|
} deriving (Functor, Foldable, Traversable)
|
|
|
|
-- | Ad-hoc helper.
|
|
-- We differientate between single row inserts ('SingleObjIns') and multiple row
|
|
-- inserts ('MultiObjIns'), but both use the same underlying representation:
|
|
-- 'AnnIns'. The only difference is which functor is used as a parameter. We use
|
|
-- '[]' for 'MultiObjIns', and we use this trivial 'Single' for 'SingleObjIns'.
|
|
|
|
newtype Single a = Single { unSingle :: a }
|
|
deriving (Functor, Foldable, Traversable)
|
|
|
|
type SingleObjIns b v = AnnIns b Single v
|
|
type MultiObjIns b v = AnnIns b [] v
|
|
|
|
-- | An insert item.
|
|
-- The object and array relationships are not unavailable when 'XNestedInserts b = XDisable'
|
|
|
|
data AnnotatedInsert (b :: BackendType) v
|
|
= AIColumn !(Column b, v)
|
|
| AIObjectRelationship !(XNestedInserts b) !(ObjRelIns b v)
|
|
| AIArrayRelationship !(XNestedInserts b) !(ArrRelIns b v)
|
|
deriving (Functor, Foldable, Traversable)
|
|
|
|
-- | One individual row to be inserted.
|
|
-- Contains the columns' values and all the matching recursive relationship inserts.
|
|
|
|
type AnnotatedInsertRow b v = [AnnotatedInsert b v]
|
|
|
|
-- | One individual relationship.
|
|
-- Unlike other types, this one is not parameterized by the type of the leaves
|
|
-- @v@, but by the kind of insert has to be performed: multi-row or single row.
|
|
-- See 'ObjRelIns' and 'ArrRelIns'.
|
|
|
|
data RelIns (b :: BackendType) a
|
|
= RelIns
|
|
{ _riAnnIns :: !a
|
|
, _riRelInfo :: !(RelInfo b)
|
|
} deriving (Show, Eq, Functor, Foldable, Traversable)
|
|
|
|
-- | Insert across an object relationship.
|
|
-- Object relationships are 1:1 relationships across tables; an insert across
|
|
-- such a relationship can only insert one single row at a time; 'RelIns' is
|
|
-- therefore parameterized by a 'SingleObjIns'.
|
|
|
|
type ObjRelIns b v = RelIns b (SingleObjIns b v)
|
|
|
|
-- | Insert across an array relationship.
|
|
-- Array relationships are 1:* relationships across tables; an insert across
|
|
-- such a relationship may therefore contain multiple rows; 'RelIns' is
|
|
-- therefore parameterized by a 'MultiObjIns'.
|
|
|
|
type ArrRelIns b v = RelIns b (MultiObjIns b v)
|
|
|
|
|
|
-- Conflict resolution options
|
|
|
|
data ConflictTarget (b :: BackendType)
|
|
= CTColumn ![Column b]
|
|
| CTConstraint !(ConstraintName b)
|
|
deriving instance Backend b => Show (ConflictTarget b)
|
|
deriving instance Backend b => Eq (ConflictTarget b)
|
|
|
|
data ConflictClauseP1 (b :: BackendType) v
|
|
= CP1DoNothing !(Maybe (ConflictTarget b))
|
|
| CP1Update !(ConflictTarget b) ![Column b] !(PreSetColsG b v) (AnnBoolExp b v)
|
|
deriving (Functor, Foldable, Traversable)
|
|
|
|
|
|
-- | Old-style representation used for non-recursive insertions.
|
|
-- This is the representation used by RQL.DML, instead of the new fancy
|
|
-- recursive one present in this file. Postgres supports both representations,
|
|
-- and actually translates recursive queries that do not have any relationships
|
|
-- into this representation first.
|
|
|
|
data InsertQueryP1 (b :: BackendType)
|
|
= InsertQueryP1
|
|
{ iqp1Table :: !(TableName b)
|
|
, iqp1Cols :: ![Column b]
|
|
, iqp1Tuples :: ![[SQLExpression b]]
|
|
, iqp1Conflict :: !(Maybe (ConflictClauseP1 b (SQLExpression b)))
|
|
, iqp1CheckCond :: !(AnnBoolExpSQL b, Maybe (AnnBoolExpSQL b))
|
|
, iqp1Output :: !(MutationOutput b)
|
|
, iqp1AllCols :: ![ColumnInfo b]
|
|
}
|
|
|
|
-- Template Haskell related
|
|
$(makePrisms ''AnnotatedInsert)
|
|
|
|
getInsertColumns :: AnnotatedInsertRow b v -> [(Column b, v)]
|
|
getInsertColumns = mapMaybe (^? _AIColumn)
|
|
|
|
getInsertObjectRelationships :: AnnotatedInsertRow b v -> [ObjRelIns b v]
|
|
getInsertObjectRelationships = mapMaybe (fmap snd . (^? _AIObjectRelationship))
|
|
|
|
getInsertArrayRelationships :: AnnotatedInsertRow b v -> [ArrRelIns b v]
|
|
getInsertArrayRelationships = mapMaybe (fmap snd . (^? _AIArrayRelationship))
|