这是indexloc提供的服务,不要输入任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions docs/graphql/manual/api-reference/graphql-api/mutation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Insert / upsert syntax
**E.g. INSERT**:

.. code-block:: graphql

mutation insert_article {
insert_article(
objects: [
Expand All @@ -73,7 +73,7 @@ Insert / upsert syntax
**E.g. UPSERT**:

.. code-block:: graphql

mutation upsert_author {
insert_author (
objects: [
Expand Down Expand Up @@ -161,7 +161,7 @@ Update syntax
**E.g. UPDATE**:

.. code-block:: graphql

mutation update_author{
update_author(
where: {id: {_eq: 3}},
Expand Down Expand Up @@ -212,7 +212,7 @@ Delete syntax
**E.g. DELETE**:

.. code-block:: graphql

mutation delete_articles {
delete_article(
where: {author: {id: {_eq: 7}}}
Expand All @@ -226,7 +226,7 @@ Delete syntax


.. note::

For more examples and details of usage, please see :doc:`this <../../mutations/index>`.

Syntax definitions
Expand Down Expand Up @@ -287,7 +287,7 @@ E.g.:
E.g.:

.. code-block:: graphql

objects: [
{
title: "Software is eating the world",
Expand All @@ -310,10 +310,11 @@ permissions before editing an existing row in case of a conflict. Hence the conf
table has *update* permissions defined.

.. code-block:: none

on_conflict: {
constraint: table_constraint!
update_columns: [table_update_column!]!
where: table_bool_exp
}

E.g.:
Expand All @@ -323,6 +324,7 @@ E.g.:
on_conflict: {
constraint: author_name_key
update_columns: [name]
where: {id: {_lt: 1}}
}

.. _whereArgExp:
Expand Down
51 changes: 49 additions & 2 deletions docs/graphql/manual/mutations/upsert.rst
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ The upsert functionality is sometimes confused with the update functionality. Ho
differently. An upsert mutation is used in the case when it's not clear if the respective row is already present
in the database. If it's known that the row is present in the database, ``update`` is the functionality to use.

For an upsert, **all columns need to be passed**.
For an upsert, **all columns need to be passed**.

**How it works**

1. Postgres tries to insert a row (hence all the columns need to be present)
1. Postgres tries to insert a row (hence all the columns need to be present)

2. If this fails because of some constraint, it updates the specified columns

Expand Down Expand Up @@ -104,6 +104,53 @@ the columns specified in ``update_columns``:

The ``published_on`` column is left unchanged as it wasn't present in ``update_columns``.

Update selected columns on conflict using a filter
--------------------------------------------------
Insert a new object in the ``article`` table, or if the primary key constraint ``article_pkey`` is violated, update
the columns specified in ``update_columns`` only if the provided ``where`` condition is met:


.. graphiql::
:view_only:
:query:
mutation upsert_article {
insert_article (
objects: [
{
id: 2,
published_on: "2018-10-12"
}
],
on_conflict: {
constraint: article_pkey,
update_columns: [published_on],
where: {
published_on: {_lt: "2018-10-12"}
}
}
) {
returning {
id
published_on
}
}
}
:response:
{
"data": {
"insert_article": {
"returning": [
{
"id": 2,
"published_on": "2018-10-12"
}
]
}
}
}

The ``published_on`` column is updated only if the new value is greater than the old value.

Ignore request on conflict
--------------------------
If ``update_columns`` is an **empty array** then the GraphQL engine ignores changes on conflict. Insert a new object into
Expand Down
19 changes: 14 additions & 5 deletions server/src-lib/Hasura/GraphQL/Resolve/Insert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module Hasura.GraphQL.Resolve.Insert
(convertInsert)
where

import Control.Arrow ((>>>))
import Data.Has
import Hasura.EncJSON
import Hasura.Prelude
Expand All @@ -21,6 +22,7 @@ import qualified Hasura.RQL.DML.Returning as RR

import qualified Hasura.SQL.DML as S

import Hasura.GraphQL.Resolve.BoolExp
import Hasura.GraphQL.Resolve.Context
import Hasura.GraphQL.Resolve.InputValue
import Hasura.GraphQL.Resolve.Mutation
Expand Down Expand Up @@ -85,7 +87,7 @@ data AnnInsObj
} deriving (Show, Eq)

mkAnnInsObj
:: (MonadResolve m, Has InsCtxMap r, MonadReader r m)
:: (MonadResolve m, Has InsCtxMap r, MonadReader r m, Has FieldMap r)
=> RelationInfoMap
-> PGColGNameMap
-> AnnGObject
Expand All @@ -96,7 +98,7 @@ mkAnnInsObj relInfoMap allColMap annObj =
emptyInsObj = AnnInsObj [] [] []

traverseInsObj
:: (MonadResolve m, Has InsCtxMap r, MonadReader r m)
:: (MonadResolve m, Has InsCtxMap r, MonadReader r m, Has FieldMap r)
=> RelationInfoMap
-> PGColGNameMap
-> (G.Name, AnnInpVal)
Expand Down Expand Up @@ -159,7 +161,7 @@ traverseInsObj rim allColMap (gName, annVal) defVal@(AnnInsObj cols objRels arrR
bool withNonEmptyArrData (return defVal) $ null arrDataVals

parseOnConflict
:: (MonadResolve m)
:: (MonadResolve m, MonadReader r m, Has FieldMap r)
=> QualifiedTable
-> Maybe UpdPermForIns
-> PGColGNameMap
Expand All @@ -178,8 +180,10 @@ parseOnConflict tn updFiltrM allColMap val = withPathK "on_conflict" $
updFltrRes <- traverseAnnBoolExp
(convPartialSQLExp sessVarFromCurrentSetting)
updFiltr
return $ RI.CP1Update constraint updCols preSetRes $
toSQLBoolExp (S.mkQual tn) updFltrRes
whereExp <- parseWhereExp obj
let updateBoolExp = toSQLBoolExp (S.mkQual tn) updFltrRes
whereCondition = S.BEBin S.AndOp updateBoolExp whereExp
return $ RI.CP1Update constraint updCols preSetRes whereCondition

where
getUpdCols o = do
Expand All @@ -193,6 +197,11 @@ parseOnConflict tn updFiltrM allColMap val = withPathK "on_conflict" $
(_, enumVal) <- asEnumVal v
return $ ConstraintName $ G.unName $ G.unEnumValue enumVal

parseWhereExp =
OMap.lookup "where"
>>> traverse (parseBoolExp >=> traverse (traverse resolveValTxt))
>>> fmap (maybe (S.BELit True) (toSQLBoolExp (S.mkQual tn)))

toSQLExps
:: (MonadError QErr m, MonadState PrepArgs m)
=> [PGColWithValue]
Expand Down
7 changes: 6 additions & 1 deletion server/src-lib/Hasura/GraphQL/Schema/Mutation/Insert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import qualified Data.HashMap.Strict as Map
import qualified Language.GraphQL.Draft.Syntax as G

import Hasura.GraphQL.Resolve.Types
import Hasura.GraphQL.Schema.BoolExp
import Hasura.GraphQL.Schema.Common
import Hasura.GraphQL.Schema.Mutation.Common
import Hasura.GraphQL.Validate.Types
Expand Down Expand Up @@ -124,14 +125,15 @@ mkInsInp tn insCols relInfoMap =
input table_on_conflict {
constraint: table_constraint!
update_columns: [table_column!]
where: table_bool_exp
}

-}

mkOnConflictInp :: QualifiedTable -> InpObjTyInfo
mkOnConflictInp tn =
mkHsraInpTyInfo (Just desc) (mkOnConflictInpTy tn) $ fromInpValL
[constraintInpVal, updateColumnsInpVal]
[constraintInpVal, updateColumnsInpVal, whereInpVal]
where
desc = G.Description $
"on conflict condition type for table " <>> tn
Expand All @@ -141,6 +143,9 @@ mkOnConflictInp tn =

updateColumnsInpVal = InpValInfo Nothing (G.Name "update_columns") Nothing $
G.toGT $ G.toNT $ G.toLT $ G.toNT $ mkUpdColumnInpTy tn

whereInpVal = InpValInfo Nothing (G.Name "where") Nothing $
G.toGT $ mkBoolExpTy tn
{-

insert_table(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
- description: Upsert into orders only if new value is greater than old value
url: /v1/graphql
status: 200
response:
data:
insert_orders:
affected_rows: 0
query:
variables:
placed: '2017-08-18 14:22:11.802755+02'
query: |
mutation ($placed: timestamptz) {
insert_orders(
objects: [{id: 1, placed: $placed}]
on_conflict: {
constraint:orders_pkey
update_columns: [placed]
where: {placed: {_lt: $placed}}
}
){
affected_rows
}
}

- description: Upsert into orders only if new value is greater than old value
url: /v1/graphql
status: 200
response:
data:
insert_orders:
affected_rows: 1
query:
variables:
placed: '2017-08-20 14:22:11.802755+02'
query: |
mutation ($placed: timestamptz) {
insert_orders(
objects: [{id: 1, placed: $placed}]
on_conflict: {
constraint:orders_pkey
update_columns: [placed]
where: {placed: {_lt: $placed}}
}
){
affected_rows
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,9 @@ args:
- content: Sample article content
title: Article 3

#Insert into orders
- type: insert
args:
table: orders
objects:
- placed: '2017-08-19 14:22:11.802755+02'
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
description: Upsert into resident table only if age is greater
url: /v1/graphql
status: 200
response:
data:
insert_resident:
affected_rows: 1
returning:
- id: 6
age: 23
query:
variables:
age: 23
query: |
mutation ($age:Int){
insert_resident(
objects: [
{
id: 6
age: $age
name: "Resident 6"
}
]
on_conflict:{
constraint: resident_pkey
update_columns: [age]
where: {age: {_lt: $age}}
}
){
affected_rows
returning{
id
age
}
}
}
6 changes: 6 additions & 0 deletions server/tests-py/test_graphql_mutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ def test_err_unexpected_action(self, hge_ctx, transport):
def test_err_unexpected_constraint(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + "/article_unexpected_on_conflict_constraint_error.yaml")

def test_order_on_conflict_where(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/order_on_conflict_where.yaml')

@classmethod
def dir(cls):
return "queries/graphql_mutation/insert/onconflict"
Expand Down Expand Up @@ -120,6 +123,9 @@ def test_resident_infant_role_insert_fail(self, hge_ctx, transport):
def test_resident_5_modifies_resident_6_upsert(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + "/resident_5_modifies_resident_6_upsert.yaml")

def test_resident_on_conflict_where(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + "/resident_on_conflict_where.yaml")

def test_blog_on_conflict_update_preset(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + "/blog_on_conflict_update_preset.yaml")

Expand Down