mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-17 20:41:49 +03:00
09ae6af337
> ### Description > Insert mutations for MSSQL backend. This PR implements execution logic. ### Changelog - [x] `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 - [x] Tests ### Related Issues -> Close https://github.com/hasura/graphql-engine-mono/issues/2114 ### Steps to test and verify > Track a MSSQL table and perform the generated insert mutation to test. PR-URL: https://github.com/hasura/graphql-engine-mono/pull/2248 Co-authored-by: Abby Sassel <3883855+sassela@users.noreply.github.com> Co-authored-by: Philip Lykke Carlsen <358550+plcplc@users.noreply.github.com> GitOrigin-RevId: 936f138c80d7a928180e6e7b0c4da64ecc1f7ebc
139 lines
5.8 KiB
Haskell
139 lines
5.8 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 Control.Lens ((^?))
|
|
import Control.Lens.TH (makePrisms)
|
|
import Data.Kind (Type)
|
|
import Hasura.Prelude
|
|
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 (XOnConflict b, ConflictClauseP1 b v)),
|
|
_aiCheckCond :: !(AnnBoolExp b v, Maybe (AnnBoolExp b v)),
|
|
_aiTableCols :: ![ColumnInfo b],
|
|
_aiDefVals :: !(PreSetColsG b v),
|
|
_aiExtraInsertData :: !(ExtraInsertData b)
|
|
}
|
|
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))
|