docs: adding mssql insert and upsert docs

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/3052
Co-authored-by: Rikin Kachhia <54616969+rikinsk@users.noreply.github.com>
GitOrigin-RevId: ad54607093aa5a353c2fbc01b51558ab192bdef9
This commit is contained in:
Gil Mizrahi 2022-01-05 18:38:39 +02:00 committed by hasura-bot
parent 0406c8ad86
commit 22a8f15017
5 changed files with 643 additions and 17 deletions

View File

@ -35,12 +35,12 @@ Here are 2 ways you can get started with Hasura and SQL Server:
Supported features
------------------
Hasura currently supports queries, subscriptions, relationships and permissions on MS SQL Server.
Hasura currently supports queries, subscriptions, inserts, deletes, relationships and permissions on MS SQL Server.
Next up on our roadmap for Hasura + SQL Server:
- Support for stored procedures & functions (`#7073 <https://github.com/hasura/graphql-engine/issues/7073>`__)
- Mutations: Run inserts, updates, stored procedures and transactions securely on SQL Server over a GraphQL API (`#7074 <https://github.com/hasura/graphql-engine/issues/7074>`__)
- Mutations: Run updates, stored procedures and transactions securely on SQL Server over a GraphQL API (`#7074 <https://github.com/hasura/graphql-engine/issues/7074>`__)
- Event triggers: Trigger HTTP webhooks with atomic capture and atleast once guarantee whenever data changes inside the database (`#7075 <https://github.com/hasura/graphql-engine/issues/7075>`__)
- Remote Joins: Join models in SQL Server to models from other API services (GraphQL or REST) (`#7076 <https://github.com/hasura/graphql-engine/issues/7076>`__)
@ -75,8 +75,4 @@ Know more
:maxdepth: 1
:titlesonly:
Schema <schema/index>
Queries <queries/index>
Mutations <mutations/index>
Subscriptions <subscriptions/index>
Supported MS SQL Server types <mssql-types>

View File

@ -31,14 +31,20 @@ The following types of mutation requests are possible:
.. toctree::
:maxdepth: 1
Insert <insert>
Upsert <upsert>
Delete <delete>
.. note::
Support for the update mutation will be added soon.
.. TODO: DBCOMPATIBILITY
.. toctree::
:maxdepth: 1
Insert <insert>
Upsert <upsert>
Update <update>
multiple-mutations

View File

@ -0,0 +1,380 @@
.. meta::
:description: Insert an object into MS SQL Server using a mutation
:keywords: hasura, docs, ms sql server, mutation, insert
.. _ms_sql_server_insert:
MS SQL Server: Insert mutation
==============================
.. contents:: Table of contents
:backlinks: none
:depth: 1
:local:
Auto-generated insert mutation schema
-------------------------------------
**For example**, the auto-generated schema for the insert mutation field for a table ``article`` looks like the following:
.. code-block:: graphql
insert_article (
objects: [article_insert_input!]!
if_matched: article_if_matched
): article_mutation_response
# response of any mutation on the table "article"
type article_mutation_response {
# number of affected rows by the mutation
affected_rows: Int!
# data of the affected rows by the mutation
returning: [article!]!
}
# single object insert
insert_article_one (
object: article_insert_input!
if_matched: article_if_matched
): article
As you can see from the schema:
- ``objects`` argument is mandatory and you can pass multiple ``objects`` to the mutation.
- You can pass an ``if_matched`` argument to convert the mutation to an :ref:`upsert mutation <ms_sql_server_upsert>`.
- You can return the number of affected rows and the affected objects (with nested objects) in the response.
- You can use the single object insert to get the inserted object directly as the mutation response.
..
See the :ref:`insert mutation API reference <insert_upsert_syntax>` for the full specifications.
.. note::
If a table is not in the ``dbo`` MS SQL Server schema, the insert mutation field will be of the format
``insert_<schema_name>_<table_name>``.
Insert a single object
----------------------
**Example:** Insert a new ``article`` object and return the inserted article object in the response:
.. graphiql::
:view_only:
:query:
mutation insert_single_article {
insert_article_one(
object: {
title: "Article 1",
content: "Sample article content",
author_id: 3
}
) {
id
title
}
}
:response:
{
"data": {
"insert_article_one": {
"id": 21,
"title": "Article 1"
}
}
}
Using variables:
.. graphiql::
:view_only:
:query:
mutation insert_single_article($object: article_insert_input! ) {
insert_article_one(object: $object) {
id
title
}
}
:response:
{
"data": {
"insert_article_one": {
"id": 21,
"title": "Article 1"
}
}
}
:variables:
{
"object": {
"title": "Article 1",
"content": "Sample article content",
"author_id": 3
}
}
.. note::
The ``insert_<object>_one`` mutation will **only** be available if you have select permissions on the table, as it returns the inserted row.
Insert multiple objects of the same type in the same mutation
-------------------------------------------------------------
**Example:** Insert 2 new ``article`` objects and return both the article objects in the response:
.. graphiql::
:view_only:
:query:
mutation insert_multiple_articles {
insert_article(
objects: [
{
title: "Article 2",
content: "Sample article content",
author_id: 4
},
{
title: "Article 3",
content: "Sample article content",
author_id: 5
}
]
) {
returning {
id
title
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 2,
"returning": [
{
"id": 22,
"title": "Article 2"
},
{
"id": 23,
"title": "Article 3"
}
]
}
}
}
Using variables:
.. graphiql::
:view_only:
:query:
mutation insert_multiple_articles($objects: [article_insert_input!]! ) {
insert_article(objects: $objects) {
returning {
id
title
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 2,
"returning": [
{
"id": 22,
"title": "Article 2"
},
{
"id": 23,
"title": "Article 3"
}
]
}
}
}
:variables:
{
"objects": [
{
"title": "Article 2",
"content": "Sample article content",
"author_id": 4
},
{
"title": "Article 3",
"content": "Sample article content",
"author_id": 5
}
]
}
Insert an object and get a nested object in response
----------------------------------------------------
**Example:** Insert a new ``article`` object and return the inserted article object with its author in the response:
.. graphiql::
:view_only:
:query:
mutation insert_article {
insert_article(
objects: [
{
title: "Article 1",
content: "Sample article content",
author_id: 3
}
]
) {
returning {
id
title
author {
id
name
}
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 1,
"returning": [
{
"id": 21,
"title": "Article 1",
"author": {
"id": 3,
"name": "Sidney"
}
}
]
}
}
}
Set a field to its default value during insert
----------------------------------------------
To set a field to its ``default`` value, just omit it from the input object, irrespective of the
default value configuration i.e. via MS SQL Server defaults or using column presets.
**Example:** If the default value of ``id`` is set to auto-incrementing integer, there's no need to pass the ``id`` field to the input object:
.. graphiql::
:view_only:
:query:
mutation insert_article_with_def_id {
insert_article(
objects: [
{
title: "Article 1",
content: "Sample article content",
author_id: 3
}
]
) {
returning {
id
title
}
}
}
:response:
{
"data": {
"insert_article": {
"affected_rows": 1,
"returning": [
{
"id": 21,
"title": "Article 1"
}
]
}
}
}
Set a field to NULL during insert
---------------------------------
If a field is ``nullable`` in the database, to set its value to ``null``, either pass its value as ``null`` or
just omit it from the input object.
**Example:** If ``age`` is a nullable field, to set it to ``null``, either don't pass the age field to the input object
or pass it as ``null``:
.. graphiql::
:view_only:
:query:
mutation insert_author_with_null_age {
insert_author(
objects: [
{
name: "Jeff"
}
]
) {
returning {
id
name
age
}
}
}
:response:
{
"data": {
"insert_author": {
"returning": [
{
"id": 11,
"name": "Jeff",
"age": null
}
]
}
}
}
OR
.. graphiql::
:view_only:
:query:
mutation insert_author_with_null_age {
insert_author(
objects: [
{
name: "Jeff",
age: null
}
]
) {
returning {
id
name
age
}
}
}
:response:
{
"data": {
"insert_author": {
"returning": [
{
"id": 11,
"name": "Jeff",
"age": null
}
]
}
}
}

View File

@ -0,0 +1,244 @@
.. meta::
:description: Use upsert mutations on MS SQL Server with Hasura
:keywords: hasura, docs, ms sql server, mutation, upsert
.. _ms_sql_server_upsert:
MS SQL Server: Upsert mutation
==============================
.. contents:: Table of contents
:backlinks: none
:depth: 1
:local:
Introduction
------------
An upsert query ensures the given set of rows are present in the database, inserting new rows or updating existing
rows as necessary, subject to certain criteria.
Convert insert mutation to upsert
---------------------------------
.. note::
Only tables with **update** permissions are **upsertable**. i.e. a table's update permissions are respected
before updating an existing row in case of a match.
To convert an :ref:`insert mutation <ms_sql_server_insert>` into an upsert, you need to use the ``if_matched`` argument to specify:
- the **columns to be matched** for each row using the ``match_columns`` field.
- the **columns to be updated** in the case of a match using the ``update_columns`` field.
- a **condition** for updating the column using the ``where`` field, and
The value of the ``update_columns`` field determines the behaviour of the upsert request as shown via the use cases
below.
Upsert is not a substitute for update
-------------------------------------
The upsert functionality is sometimes confused with the update functionality. However, they work slightly
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 that are necessary for an insert are required**.
**How it works**
1. MS SQL Server tries to insert a row (hence all the required columns need to be present)
2. If this fails because of some match, it updates the specified columns
If not all required columns are present, an error like ``NULL value unexpected for <not-specified-column>`` can occur.
Update selected columns on match
--------------------------------
The ``update_columns`` field can be used to specify which columns to update in case a match occurs.
**Example**: Insert a new object in the ``article`` table or, if the value of the ``title`` column
matches a a ``title`` value in an existing row, update the ``content`` column of the existing article:
.. graphiql::
:view_only:
:query:
mutation upsert_article {
insert_article (
objects: [
{
title: "Article 1",
content: "Updated article 1 content",
published_on: "2018-10-12"
}
],
if_matched: {
match_columns: title,
update_columns: content
}
) {
returning {
id
title
content
published_on
}
}
}
:response:
{
"data": {
"insert_article": {
"returning": [
{
"id": 1,
"title": "Article 1",
"content": "Updated article 1 content",
"published_on": "2018-06-15"
}
]
}
}
}
Note that the ``published_on`` column is left unchanged as it wasn't present in ``update_columns``.
.. note::
If ``match_columns`` is an **empty array** there is no basis for comparing new rows to existing rows.
Thus the mutation will always **insert** the values and will never update any rows.
**Example**: Insert a new object into the article table, will not match on columns because
none are specified.
.. graphiql::
:view_only:
:query:
mutation upsert_article {
insert_article (
objects: [
{
title: "Article 1",
content: "Article 1 content",
published_on: "2018-10-12"
}
],
if_matched: {
match_columns: [],
update_columns: content
}
) {
returning {
id
title
content
published_on
}
}
}
:response:
{
"data": {
"insert_article": {
"returning": [
{
"id": 3,
"title": "Article 1",
"content": "Article 1 content",
"published_on": "2018-06-15"
}
]
}
}
}
This query is equivalent to a simple insert without an ``if_matched`` clause.
Update selected columns on match subject to a filter
----------------------------------------------------
A ``where`` condition can be added to the ``if_matched`` clause to check a condition before making the update in case a
match occurs.
**Example**: Insert a new object in the ``article`` table, or if the value of the ``title`` column
matches a a ``title`` value in an existing row, update the ``published_on`` column specified
in ``update_columns`` only if the previous ``published_on``
value is lesser than the new value:
.. graphiql::
:view_only:
:query:
mutation upsert_article {
insert_article (
objects: [
{
title: "Article 2",
published_on: "2018-10-12"
}
],
if_matched: {
match_columns: title,
update_columns: published_on,
where: {
published_on: {_lt: "2018-10-12"}
}
}
) {
returning {
id
title
published_on
}
}
}
:response:
{
"data": {
"insert_article": {
"returning": [
{
"id": 2,
"title": "Article 2",
"published_on": "2018-10-12"
}
]
}
}
}
Ignore request on match
-----------------------
If ``update_columns`` is an **empty array** then on match the changes are ignored.
**Example**: Insert a new author object into the author table, unless there already exists an author with the same name.
.. graphiql::
:view_only:
:query:
mutation upsert_author {
insert_author(
objects: [
{ name: "John" }
],
if_matched: {
match_columns: name,
update_columns: []
}
) {
affected_rows
}
}
:response:
{
"data": {
"insert_author": {
"affected_rows": 0
}
}
}
In this case, the insert mutation is ignored because there is a match and ``update_columns`` is empty.

View File

@ -11,7 +11,7 @@ Postgres: Upsert mutation
:backlinks: none
:depth: 1
:local:
Introduction
------------
@ -69,7 +69,7 @@ Update selected columns on conflict
The ``update_columns`` field can be used to specify which columns to update in case a conflict occurs.
**Example**: Insert a new object in the ``article`` table or, if the unique constraint ``article_title_key`` is
**Example**: Insert a new object in the ``article`` table or, if the unique constraint ``article_title_key`` is
violated, update the ``content`` column of the existing article:
.. graphiql::
@ -80,7 +80,7 @@ violated, update the ``content`` column of the existing article:
objects: [
{
title: "Article 1",
content: "Article 1 content",
content: "Updated article 1 content",
published_on: "2018-10-12"
}
],
@ -105,7 +105,7 @@ violated, update the ``content`` column of the existing article:
{
"id": 1,
"title": "Article 1",
"content": "Article 1 content",
"content": "Updated article 1 content",
"published_on": "2018-06-15"
}
]
@ -118,11 +118,11 @@ Note that the ``published_on`` column is left unchanged as it wasn't present in
Update selected columns on conflict using a filter
--------------------------------------------------
A ``where`` condition can be added to the ``on_conflict`` clause to check a condition before making the update in case a
A ``where`` condition can be added to the ``on_conflict`` clause to check a condition before making the update in case a
conflict occurs
**Example**: Insert a new object in the ``article`` table, or if the unique key constraint ``article_title_key`` is
violated, update the ``published_on`` columns specified in ``update_columns`` only if the previous ``published_on``
violated, update the ``published_on`` column specified in ``update_columns`` only if the previous ``published_on``
value is lesser than the new value:
.. graphiql::
@ -168,9 +168,9 @@ value is lesser than the new value:
Ignore request on conflict
--------------------------
If ``update_columns`` is an **empty array** then on conflict the changes are ignored.
If ``update_columns`` is an **empty array** then on conflict the changes are ignored.
**Example**: Insert a new object into the author table or, if the unique constraint ``author_name_key`` is violated,
**Example**: Insert a new object into the author table or, if the unique constraint ``author_name_key`` is violated,
ignore the request.
.. graphiql::
@ -205,7 +205,7 @@ Upsert in nested mutations
--------------------------
You can specify the ``on_conflict`` clause while inserting nested objects:
**Example**:
**Example**:
.. graphiql::
:view_only: