mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 09:22:43 +03:00
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:
parent
0406c8ad86
commit
22a8f15017
@ -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>
|
||||
|
@ -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
|
||||
|
380
docs/graphql/core/databases/ms-sql-server/mutations/insert.rst
Normal file
380
docs/graphql/core/databases/ms-sql-server/mutations/insert.rst
Normal 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
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
244
docs/graphql/core/databases/ms-sql-server/mutations/upsert.rst
Normal file
244
docs/graphql/core/databases/ms-sql-server/mutations/upsert.rst
Normal 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.
|
@ -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"
|
||||
}
|
||||
]
|
||||
@ -122,7 +122,7 @@ A ``where`` condition can be added to the ``on_conflict`` clause to check a cond
|
||||
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::
|
||||
|
Loading…
Reference in New Issue
Block a user