Add nested insert and update mutations with support for connect and disconnect
This commit is contained in:
parent
6831d3f56f
commit
482203ba05
2
Makefile
2
Makefile
|
@ -41,7 +41,7 @@ changelog: $(GITCHGLOG)
|
||||||
$(GOLANGCILINT):
|
$(GOLANGCILINT):
|
||||||
@GO111MODULE=off curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(GOPATH)/bin v1.21.0
|
@GO111MODULE=off curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh| sh -s -- -b $(GOPATH)/bin v1.21.0
|
||||||
|
|
||||||
lint: $(GOMETALINTER)
|
lint: $(GOLANGCILINT)
|
||||||
@golangci-lint run ./... --skip-dirs-use-default
|
@golangci-lint run ./... --skip-dirs-use-default
|
||||||
|
|
||||||
BINARY := super-graph
|
BINARY := super-graph
|
||||||
|
|
|
@ -281,4 +281,475 @@ mutation {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"update": {
|
||||||
|
"name": "my_name",
|
||||||
|
"description": "my_desc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(id: 15, update: $update, where: {id: {eq: 1}}) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"update": {
|
||||||
|
"name": "my_name",
|
||||||
|
"description": "my_desc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(update: $update, where: {id: {eq: 1}}) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"update": {
|
||||||
|
"name": "my_name 2",
|
||||||
|
"description": "my_desc 2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(update: $update, where: {id: {eq: 1}}) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"sale_type": "tuutuu",
|
||||||
|
"quantity": 5,
|
||||||
|
"due_date": "now",
|
||||||
|
"customer": {
|
||||||
|
"email": "thedude1@rug.com",
|
||||||
|
"full_name": "The Dude"
|
||||||
|
},
|
||||||
|
"product": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
purchase(update: $data, id: 5) {
|
||||||
|
sale_type
|
||||||
|
quantity
|
||||||
|
due_date
|
||||||
|
customer {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
product {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"email": "thedude@rug.com",
|
||||||
|
"full_name": "The Dude",
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"product": {
|
||||||
|
"where": {
|
||||||
|
"id": 2
|
||||||
|
},
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
user(update: $data, where: {id: {eq: 8}}) {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
product {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"email": "thedude@rug.com",
|
||||||
|
"full_name": "The Dude",
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"product": {
|
||||||
|
"where": {
|
||||||
|
"id": 2
|
||||||
|
},
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query {
|
||||||
|
user(where: {id: {eq: 8}}) {
|
||||||
|
id
|
||||||
|
product {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"email": "thedude@rug.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query {
|
||||||
|
user {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"email": "booboo@demo.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(update: $data, id: 6) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"email": "booboo@demo.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query {
|
||||||
|
product(id: 6) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"email": "thedude123@rug.com",
|
||||||
|
"full_name": "The Dude",
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"product": {
|
||||||
|
"connect": {
|
||||||
|
"id": 7
|
||||||
|
},
|
||||||
|
"disconnect": {
|
||||||
|
"id": 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
user(update: $data, id: 6) {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
product {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"connect": {
|
||||||
|
"id": 5,
|
||||||
|
"email": "test@test.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(update: $data, id: 9) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"email": "thed44ude@rug.com",
|
||||||
|
"full_name": "The Dude",
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"product": {
|
||||||
|
"connect": {
|
||||||
|
"id": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
user(insert: $data) {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
product {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"connect": {
|
||||||
|
"id": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(insert: $data) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"connect": {
|
||||||
|
"id": 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Coconut",
|
||||||
|
"price": 2.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"connect": {
|
||||||
|
"id": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
products(insert: $data) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Coconut",
|
||||||
|
"price": 2.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
products(insert: $data) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"user": {
|
||||||
|
"connect": {
|
||||||
|
"id": 5,
|
||||||
|
"email": "test@test.com"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(update: $data, id: 9) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"user": {
|
||||||
|
"connect": {
|
||||||
|
"id": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(update: $data, id: 9) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"user": {
|
||||||
|
"disconnect": {
|
||||||
|
"id": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(update: $data, id: 9) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
variables {
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"user": {
|
||||||
|
"disconnect": {
|
||||||
|
"id": 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mutation {
|
||||||
|
product(update: $data, id: 2) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user_id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
136
docs/guide.md
136
docs/guide.md
|
@ -640,7 +640,7 @@ query {
|
||||||
|
|
||||||
## Mutations
|
## Mutations
|
||||||
|
|
||||||
In GraphQL mutations is the operation type for when you need to modify data. Super Graph supports the `insert`, `update`, `upsert` and `delete` database operations. Here are some examples.
|
In GraphQL mutations is the operation type for when you need to modify data. Super Graph supports the `insert`, `update`, `upsert` and `delete`. You can also do complex nested inserts and updates.
|
||||||
|
|
||||||
When using mutations the data must be passed as variables since Super Graphs compiles the query into an prepared statement in the database for maximum speed. Prepared statements are are functions in your code when called they accept arguments and your variables are passed in as those arguments.
|
When using mutations the data must be passed as variables since Super Graphs compiles the query into an prepared statement in the database for maximum speed. Prepared statements are are functions in your code when called they accept arguments and your variables are passed in as those arguments.
|
||||||
|
|
||||||
|
@ -823,7 +823,137 @@ mutation {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Using variables
|
## Nested Mutations
|
||||||
|
|
||||||
|
Often you will need to create or update multiple related items at the same time. This can be done using nested mutations. For example you might need to create a product and assign it to a user, or create a user and his products at the same time. You just have to use simple json to define you mutation and Super Graph takes care of the rest.
|
||||||
|
|
||||||
|
### Nested Insert
|
||||||
|
|
||||||
|
Create a product item first and then assign it to a user
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"connect": { "id": 5 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
mutation {
|
||||||
|
product(insert: $data) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or it's reverse, create the user first and then his product
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"email": "thedude@rug.com",
|
||||||
|
"full_name": "The Dude",
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"product": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
mutation {
|
||||||
|
user(insert: $data) {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
product {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nested Updates
|
||||||
|
|
||||||
|
Update a product item first and then assign it to a user
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"user": {
|
||||||
|
"connect": { "id": 5 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
mutation {
|
||||||
|
product(update: $data, id: 5) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Or it's reverse, update a user first and then his product
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"email": "newemail@me.com",
|
||||||
|
"full_name": "The Dude",
|
||||||
|
"product": {
|
||||||
|
"name": "Banana",
|
||||||
|
"price": 1.25,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
mutation {
|
||||||
|
user(update: $data, id: 1) {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
product {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Using variables
|
||||||
|
|
||||||
Variables (`$product_id`) and their values (`"product_id": 5`) can be passed along side the GraphQL query. Using variables makes for better client side code as well as improved server side SQL query caching. The build-in web-ui also supports setting variables. Not having to manipulate your GraphQL query string to insert values into it makes for cleaner
|
Variables (`$product_id`) and their values (`"product_id": 5`) can be passed along side the GraphQL query. Using variables makes for better client side code as well as improved server side SQL query caching. The build-in web-ui also supports setting variables. Not having to manipulate your GraphQL query string to insert values into it makes for cleaner
|
||||||
and better client side code.
|
and better client side code.
|
||||||
|
@ -845,7 +975,7 @@ fetch('http://localhost:8080/api/v1/graphql', {
|
||||||
.then(res => console.log(res.data));
|
.then(res => console.log(res.data));
|
||||||
```
|
```
|
||||||
|
|
||||||
### Full text search
|
## Full text search
|
||||||
|
|
||||||
Every app these days needs search. Enought his often means reaching for something heavy like Solr. While this will work why add complexity to your infrastructure when Postgres has really great
|
Every app these days needs search. Enought his often means reaching for something heavy like Solr. While this will work why add complexity to your infrastructure when Postgres has really great
|
||||||
and fast full text search built-in. And since it's part of Postgres it's also available in Super Graph.
|
and fast full text search built-in. And since it's part of Postgres it's also available in Super Graph.
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
|
//nolint:errcheck
|
||||||
package psql
|
package psql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
@ -27,6 +29,9 @@ func (c *compilerContext) renderInsert(qc *qcode.QCode, w io.Writer,
|
||||||
if st.Len() == 0 {
|
if st.Len() == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if insert[0] == '[' && st.Len() > 1 {
|
||||||
|
return 0, errors.New("Nested bulk insert not supported")
|
||||||
|
}
|
||||||
intf := st.Pop()
|
intf := st.Pop()
|
||||||
|
|
||||||
switch item := intf.(type) {
|
switch item := intf.(type) {
|
||||||
|
@ -38,8 +43,6 @@ func (c *compilerContext) renderInsert(qc *qcode.QCode, w io.Writer,
|
||||||
case renitem:
|
case renitem:
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
io.WriteString(c.w, `, `)
|
|
||||||
|
|
||||||
// if w := qc.Selects[0].Where; w != nil && w.Op == qcode.OpFalse {
|
// if w := qc.Selects[0].Where; w != nil && w.Op == qcode.OpFalse {
|
||||||
// io.WriteString(c.w, ` WHERE false`)
|
// io.WriteString(c.w, ` WHERE false`)
|
||||||
// }
|
// }
|
||||||
|
@ -50,7 +53,7 @@ func (c *compilerContext) renderInsert(qc *qcode.QCode, w io.Writer,
|
||||||
case itemConnect:
|
case itemConnect:
|
||||||
err = c.renderConnectStmt(qc, w, item)
|
err = c.renderConnectStmt(qc, w, item)
|
||||||
case itemUnion:
|
case itemUnion:
|
||||||
err = c.renderInsertUnionStmt(w, item)
|
err = c.renderUnionStmt(w, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -69,6 +72,7 @@ func (c *compilerContext) renderInsertStmt(qc *qcode.QCode, w io.Writer, item re
|
||||||
jt := item.data
|
jt := item.data
|
||||||
sk := nestedInsertRelColumnsMap(item.kvitem)
|
sk := nestedInsertRelColumnsMap(item.kvitem)
|
||||||
|
|
||||||
|
io.WriteString(c.w, `, `)
|
||||||
renderCteName(w, item.kvitem)
|
renderCteName(w, item.kvitem)
|
||||||
io.WriteString(w, ` AS (`)
|
io.WriteString(w, ` AS (`)
|
||||||
|
|
||||||
|
@ -171,19 +175,3 @@ func renderNestedInsertRelTables(w io.Writer, item kvitem) error {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderInsertUnionStmt(w io.Writer, item renitem) error {
|
|
||||||
renderCteName(w, item.kvitem)
|
|
||||||
io.WriteString(w, ` AS (`)
|
|
||||||
|
|
||||||
for i, v := range item.items {
|
|
||||||
if i != 0 {
|
|
||||||
io.WriteString(w, ` UNION ALL `)
|
|
||||||
}
|
|
||||||
io.WriteString(w, `SELECT * FROM `)
|
|
||||||
renderCteName(w, v)
|
|
||||||
}
|
|
||||||
io.WriteString(w, `)`)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -249,7 +249,7 @@ func nestedInsertOneToManyWithConnect(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (INSERT INTO "users" ("full_name", "email", "created_at", "updated_at") SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t RETURNING *), "products_2" AS (UPDATE "products" SET "user_id" = "users"."id" WHERE "id" = '5' RETURNING *), "products" AS (SELECT * FROM "products_2") SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (INSERT INTO "users" ("full_name", "email", "created_at", "updated_at") SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t RETURNING *), "products" AS ( UPDATE "products" SET "user_id" = "users"."id"FROM "users" WHERE ("products"."id" = '5') RETURNING "products".*) SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
|
@ -286,7 +286,7 @@ func nestedInsertOneToOneWithConnect(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users_2" AS (SELECT * FROM "users" WHERE "id" = '5' LIMIT 1 RETURNING *), "users" AS (SELECT * FROM "users_2"), "products" AS (INSERT INTO "products" ("name", "price", "created_at", "updated_at", "user_id") SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT * FROM "users" WHERE "users"."id" = '5' LIMIT 1), "products" AS (INSERT INTO "products" ("name", "price", "created_at", "updated_at", "user_id") SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
|
|
389
psql/mutate.go
389
psql/mutate.go
|
@ -24,14 +24,11 @@ const (
|
||||||
|
|
||||||
var insertTypes = map[string]itemType{
|
var insertTypes = map[string]itemType{
|
||||||
"connect": itemConnect,
|
"connect": itemConnect,
|
||||||
"_connect": itemConnect,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var updateTypes = map[string]itemType{
|
var updateTypes = map[string]itemType{
|
||||||
"connect": itemConnect,
|
"connect": itemConnect,
|
||||||
"_connect": itemConnect,
|
|
||||||
"disconnect": itemDisconnect,
|
"disconnect": itemDisconnect,
|
||||||
"_disconnect": itemDisconnect,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var noLimit = qcode.Paging{NoLimit: true}
|
var noLimit = qcode.Paging{NoLimit: true}
|
||||||
|
@ -86,9 +83,12 @@ func (co *Compiler) compileMutation(qc *qcode.QCode, w io.Writer, vars Variables
|
||||||
type kvitem struct {
|
type kvitem struct {
|
||||||
id int32
|
id int32
|
||||||
_type itemType
|
_type itemType
|
||||||
|
_ctype int
|
||||||
key string
|
key string
|
||||||
path []string
|
path []string
|
||||||
val json.RawMessage
|
val json.RawMessage
|
||||||
|
data map[string]json.RawMessage
|
||||||
|
array bool
|
||||||
ti *DBTableInfo
|
ti *DBTableInfo
|
||||||
relCP *DBRel
|
relCP *DBRel
|
||||||
relPC *DBRel
|
relPC *DBRel
|
||||||
|
@ -102,10 +102,18 @@ type renitem struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
||||||
data, array, err := jsn.Tree(item.val)
|
var data map[string]json.RawMessage
|
||||||
|
var array bool
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if item.data == nil {
|
||||||
|
data, array, err = jsn.Tree(item.val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
data, array = item.data, item.array
|
||||||
|
}
|
||||||
|
|
||||||
var unionize bool
|
var unionize bool
|
||||||
id := item.id + 1
|
id := item.id + 1
|
||||||
|
@ -155,7 +163,7 @@ func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
item.items = append(item.items, kvitem{
|
item1 := kvitem{
|
||||||
id: id,
|
id: id,
|
||||||
_type: item._type,
|
_type: item._type,
|
||||||
key: k,
|
key: k,
|
||||||
|
@ -164,7 +172,22 @@ func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
||||||
ti: ti,
|
ti: ti,
|
||||||
relCP: relCP,
|
relCP: relCP,
|
||||||
relPC: relPC,
|
relPC: relPC,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if v[0] == '{' {
|
||||||
|
item1.data, item1.array, err = jsn.Tree(v)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if v1, ok := item1.data["connect"]; ok && (v1[0] == '{' || v1[0] == '[') {
|
||||||
|
item1._ctype |= (1 << itemConnect)
|
||||||
|
}
|
||||||
|
if v1, ok := item1.data["disconnect"]; ok && (v1[0] == '{' || v1[0] == '[') {
|
||||||
|
item1._ctype |= (1 << itemDisconnect)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
item.items = append(item.items, item1)
|
||||||
id++
|
id++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,11 +217,25 @@ func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case itemUpdate:
|
||||||
|
for _, v := range item.items {
|
||||||
|
if !(v._ctype > 0 && v.relPC.Type == RelOneToOne) {
|
||||||
|
st.Push(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
st.Push(renitem{kvitem: item, array: array, data: data})
|
||||||
|
for _, v := range item.items {
|
||||||
|
if v._ctype > 0 && v.relPC.Type == RelOneToOne {
|
||||||
|
st.Push(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case itemUnion:
|
case itemUnion:
|
||||||
st.Push(renitem{kvitem: item, array: array, data: data})
|
st.Push(renitem{kvitem: item, array: array, data: data})
|
||||||
for _, v := range item.items {
|
for _, v := range item.items {
|
||||||
st.Push(v)
|
st.Push(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
for _, v := range item.items {
|
for _, v := range item.items {
|
||||||
st.Push(v)
|
st.Push(v)
|
||||||
|
@ -209,6 +246,112 @@ func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) renderUnionStmt(w io.Writer, item renitem) error {
|
||||||
|
var connect, disconnect bool
|
||||||
|
|
||||||
|
// Render only for parent-to-child relationship of one-to-many
|
||||||
|
if item.relPC.Type != RelOneToMany {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range item.items {
|
||||||
|
if v._type == itemConnect {
|
||||||
|
connect = true
|
||||||
|
} else if v._type == itemDisconnect {
|
||||||
|
disconnect = true
|
||||||
|
}
|
||||||
|
if connect && disconnect {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if connect {
|
||||||
|
io.WriteString(w, `, `)
|
||||||
|
if connect && disconnect {
|
||||||
|
renderCteNameWithSuffix(w, item.kvitem, "c")
|
||||||
|
} else {
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
}
|
||||||
|
io.WriteString(w, ` AS ( UPDATE `)
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
io.WriteString(w, ` SET `)
|
||||||
|
quoted(w, item.relPC.Right.Col)
|
||||||
|
io.WriteString(w, ` = `)
|
||||||
|
colWithTable(w, item.relPC.Left.Table, item.relPC.Left.Col)
|
||||||
|
io.WriteString(w, `FROM `)
|
||||||
|
quoted(w, item.relPC.Left.Table)
|
||||||
|
io.WriteString(w, ` WHERE`)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for _, v := range item.items {
|
||||||
|
if v._type == itemConnect {
|
||||||
|
if i != 0 {
|
||||||
|
io.WriteString(w, ` OR (`)
|
||||||
|
} else {
|
||||||
|
io.WriteString(w, ` (`)
|
||||||
|
}
|
||||||
|
if err := renderKVItemWhere(w, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
io.WriteString(w, `)`)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io.WriteString(w, ` RETURNING `)
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
io.WriteString(w, `.*)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if disconnect {
|
||||||
|
io.WriteString(w, `, `)
|
||||||
|
if connect && disconnect {
|
||||||
|
renderCteNameWithSuffix(w, item.kvitem, "d")
|
||||||
|
} else {
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
}
|
||||||
|
io.WriteString(w, ` AS ( UPDATE `)
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
io.WriteString(w, ` SET `)
|
||||||
|
quoted(w, item.relPC.Right.Col)
|
||||||
|
io.WriteString(w, ` = NULL`)
|
||||||
|
io.WriteString(w, ` FROM `)
|
||||||
|
quoted(w, item.relPC.Left.Table)
|
||||||
|
io.WriteString(w, ` WHERE`)
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
for _, v := range item.items {
|
||||||
|
if v._type == itemDisconnect {
|
||||||
|
if i != 0 {
|
||||||
|
io.WriteString(w, ` OR (`)
|
||||||
|
} else {
|
||||||
|
io.WriteString(w, ` (`)
|
||||||
|
}
|
||||||
|
if err := renderKVItemWhere(w, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
io.WriteString(w, `)`)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io.WriteString(w, ` RETURNING `)
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
io.WriteString(w, `.*), `)
|
||||||
|
}
|
||||||
|
|
||||||
|
if connect && disconnect {
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
io.WriteString(w, ` AS (`)
|
||||||
|
io.WriteString(w, `SELECT * FROM `)
|
||||||
|
renderCteNameWithSuffix(w, item.kvitem, "c")
|
||||||
|
io.WriteString(w, ` UNION ALL `)
|
||||||
|
io.WriteString(w, `SELECT * FROM `)
|
||||||
|
renderCteNameWithSuffix(w, item.kvitem, "d")
|
||||||
|
io.WriteString(w, `)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func renderInsertUpdateColumns(w io.Writer,
|
func renderInsertUpdateColumns(w io.Writer,
|
||||||
qc *qcode.QCode,
|
qc *qcode.QCode,
|
||||||
jt map[string]json.RawMessage,
|
jt map[string]json.RawMessage,
|
||||||
|
@ -346,6 +489,101 @@ func (c *compilerContext) renderUpsert(qc *qcode.QCode, w io.Writer,
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) renderConnectStmt(qc *qcode.QCode, w io.Writer,
|
||||||
|
item renitem) error {
|
||||||
|
|
||||||
|
rel := item.relPC
|
||||||
|
|
||||||
|
// Render only for parent-to-child relationship of one-to-one
|
||||||
|
if rel.Type != RelOneToOne {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
io.WriteString(w, `, `)
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
io.WriteString(c.w, ` AS (`)
|
||||||
|
|
||||||
|
io.WriteString(c.w, `SELECT * FROM `)
|
||||||
|
quoted(c.w, item.ti.Name)
|
||||||
|
io.WriteString(c.w, ` WHERE `)
|
||||||
|
if err := renderKVItemWhere(c.w, item.kvitem); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
io.WriteString(c.w, ` LIMIT 1)`)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compilerContext) renderDisconnectStmt(qc *qcode.QCode, w io.Writer,
|
||||||
|
item renitem) error {
|
||||||
|
|
||||||
|
rel := item.relPC
|
||||||
|
|
||||||
|
// Render only for parent-to-child relationship of one-to-one
|
||||||
|
if rel.Type != RelOneToOne {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
io.WriteString(w, `, `)
|
||||||
|
quoted(w, item.ti.Name)
|
||||||
|
io.WriteString(c.w, ` AS (`)
|
||||||
|
|
||||||
|
io.WriteString(c.w, `SELECT * FROM (VALUES(NULL::`)
|
||||||
|
io.WriteString(w, rel.Right.col.Type)
|
||||||
|
io.WriteString(c.w, `)) AS LOOKUP(`)
|
||||||
|
quoted(w, rel.Right.Col)
|
||||||
|
io.WriteString(c.w, `))`)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderKVItemWhere(w io.Writer, item kvitem) error {
|
||||||
|
return renderWhereFromJSON(w, item.ti.Name, item.val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderWhereFromJSON(w io.Writer, table string, val []byte) error {
|
||||||
|
var kv map[string]json.RawMessage
|
||||||
|
if err := json.Unmarshal(val, &kv); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
i := 0
|
||||||
|
for k, v := range kv {
|
||||||
|
if i != 0 {
|
||||||
|
io.WriteString(w, ` AND `)
|
||||||
|
}
|
||||||
|
colWithTable(w, table, k)
|
||||||
|
io.WriteString(w, ` = '`)
|
||||||
|
switch v[0] {
|
||||||
|
case '"':
|
||||||
|
w.Write(v[1 : len(v)-1])
|
||||||
|
default:
|
||||||
|
w.Write(v)
|
||||||
|
}
|
||||||
|
io.WriteString(w, `'`)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderCteName(w io.Writer, item kvitem) error {
|
||||||
|
io.WriteString(w, `"`)
|
||||||
|
io.WriteString(w, item.ti.Name)
|
||||||
|
if item._type == itemConnect || item._type == itemDisconnect {
|
||||||
|
io.WriteString(w, `_`)
|
||||||
|
int2string(w, item.id)
|
||||||
|
}
|
||||||
|
io.WriteString(w, `"`)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderCteNameWithSuffix(w io.Writer, item kvitem, suffix string) error {
|
||||||
|
io.WriteString(w, `"`)
|
||||||
|
io.WriteString(w, item.ti.Name)
|
||||||
|
io.WriteString(w, `_`)
|
||||||
|
io.WriteString(w, suffix)
|
||||||
|
io.WriteString(w, `"`)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func quoted(w io.Writer, identifier string) {
|
func quoted(w io.Writer, identifier string) {
|
||||||
io.WriteString(w, `"`)
|
io.WriteString(w, `"`)
|
||||||
io.WriteString(w, identifier)
|
io.WriteString(w, identifier)
|
||||||
|
@ -362,142 +600,3 @@ func joinPath(w io.Writer, path []string) {
|
||||||
io.WriteString(w, `'`)
|
io.WriteString(w, `'`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderConnectStmt(qc *qcode.QCode, w io.Writer,
|
|
||||||
item renitem) error {
|
|
||||||
|
|
||||||
rel := item.relPC
|
|
||||||
|
|
||||||
renderCteName(c.w, item.kvitem)
|
|
||||||
io.WriteString(c.w, ` AS (`)
|
|
||||||
|
|
||||||
// Render either select or update sql based on parent-to-child
|
|
||||||
// relationship
|
|
||||||
switch rel.Type {
|
|
||||||
case RelOneToOne:
|
|
||||||
io.WriteString(c.w, `SELECT * FROM `)
|
|
||||||
quoted(c.w, item.ti.Name)
|
|
||||||
io.WriteString(c.w, ` WHERE `)
|
|
||||||
if err := renderKVItemWhere(c.w, item.kvitem); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
io.WriteString(c.w, ` LIMIT 1`)
|
|
||||||
|
|
||||||
case RelOneToMany:
|
|
||||||
// UPDATE films SET kind = 'Dramatic' WHERE kind = 'Drama';
|
|
||||||
io.WriteString(c.w, `UPDATE `)
|
|
||||||
quoted(c.w, item.ti.Name)
|
|
||||||
io.WriteString(c.w, ` SET `)
|
|
||||||
quoted(c.w, rel.Right.Col)
|
|
||||||
io.WriteString(c.w, ` = `)
|
|
||||||
colWithTable(c.w, rel.Left.Table, rel.Left.Col)
|
|
||||||
io.WriteString(c.w, ` WHERE `)
|
|
||||||
if err := renderKVItemWhere(c.w, item.kvitem); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsuppported relationship %s", rel)
|
|
||||||
}
|
|
||||||
|
|
||||||
io.WriteString(c.w, ` RETURNING *)`)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *compilerContext) renderDisconnectStmt(qc *qcode.QCode, w io.Writer,
|
|
||||||
item renitem) error {
|
|
||||||
|
|
||||||
renderCteName(c.w, item.kvitem)
|
|
||||||
io.WriteString(c.w, ` AS (`)
|
|
||||||
|
|
||||||
io.WriteString(c.w, `UPDATE `)
|
|
||||||
quoted(c.w, item.ti.Name)
|
|
||||||
io.WriteString(c.w, ` SET `)
|
|
||||||
quoted(c.w, item.relPC.Right.Col)
|
|
||||||
io.WriteString(c.w, ` = NULL `)
|
|
||||||
io.WriteString(c.w, ` WHERE `)
|
|
||||||
|
|
||||||
// Render either select or update sql based on parent-to-child
|
|
||||||
// relationship
|
|
||||||
switch item.relPC.Type {
|
|
||||||
case RelOneToOne:
|
|
||||||
if err := renderRelEquals(c.w, item.relPC); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
case RelOneToMany:
|
|
||||||
if err := renderRelEquals(c.w, item.relPC); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
io.WriteString(c.w, ` AND `)
|
|
||||||
|
|
||||||
if err := renderKVItemWhere(c.w, item.kvitem); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unsuppported relationship %s", item.relPC)
|
|
||||||
}
|
|
||||||
|
|
||||||
io.WriteString(c.w, ` RETURNING *)`)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderKVItemWhere(w io.Writer, item kvitem) error {
|
|
||||||
return renderWhereFromJSON(w, item.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderWhereFromJSON(w io.Writer, val []byte) error {
|
|
||||||
var kv map[string]json.RawMessage
|
|
||||||
if err := json.Unmarshal(val, &kv); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
i := 0
|
|
||||||
for k, v := range kv {
|
|
||||||
if i != 0 {
|
|
||||||
io.WriteString(w, ` AND `)
|
|
||||||
}
|
|
||||||
quoted(w, k)
|
|
||||||
io.WriteString(w, ` = '`)
|
|
||||||
switch v[0] {
|
|
||||||
case '"':
|
|
||||||
w.Write(v[1 : len(v)-1])
|
|
||||||
default:
|
|
||||||
w.Write(v)
|
|
||||||
}
|
|
||||||
io.WriteString(w, `'`)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderRelEquals(w io.Writer, rel *DBRel) error {
|
|
||||||
switch rel.Type {
|
|
||||||
case RelOneToOne:
|
|
||||||
colWithTable(w, rel.Left.Table, rel.Left.Col)
|
|
||||||
io.WriteString(w, ` = `)
|
|
||||||
colWithTable(w, rel.Right.Table, rel.Right.Col)
|
|
||||||
|
|
||||||
case RelOneToMany:
|
|
||||||
colWithTable(w, rel.Right.Table, rel.Right.Col)
|
|
||||||
io.WriteString(w, ` = `)
|
|
||||||
colWithTable(w, rel.Left.Table, rel.Left.Col)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderCteName(w io.Writer, item kvitem) error {
|
|
||||||
io.WriteString(w, `"`)
|
|
||||||
io.WriteString(w, item.ti.Name)
|
|
||||||
if item._type == itemConnect || item._type == itemDisconnect {
|
|
||||||
io.WriteString(w, `_`)
|
|
||||||
int2string(w, item.id)
|
|
||||||
}
|
|
||||||
io.WriteString(w, `"`)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -85,7 +85,7 @@ func delete(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "products" AS (DELETE FROM "products" WHERE (("products"."price") > 0) AND (("products"."price") < 8) AND (("products"."id") = 1) RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "products" AS (DELETE FROM "products" WHERE (("products"."price") > 0) AND (("products"."price") < 8) AND (("products"."id") = 1) RETURNING "products".*)SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"update": json.RawMessage(` { "name": "my_name", "description": "my_desc" }`),
|
"update": json.RawMessage(` { "name": "my_name", "description": "my_desc" }`),
|
||||||
|
|
|
@ -38,11 +38,13 @@ type DBRel struct {
|
||||||
Through string
|
Through string
|
||||||
ColT string
|
ColT string
|
||||||
Left struct {
|
Left struct {
|
||||||
|
col *DBColumn
|
||||||
Table string
|
Table string
|
||||||
Col string
|
Col string
|
||||||
Array bool
|
Array bool
|
||||||
}
|
}
|
||||||
Right struct {
|
Right struct {
|
||||||
|
col *DBColumn
|
||||||
Table string
|
Table string
|
||||||
Col string
|
Col string
|
||||||
Array bool
|
Array bool
|
||||||
|
@ -166,10 +168,12 @@ func (s *DBSchema) updateRelationships(t DBTable, cols []DBColumn) error {
|
||||||
rel1 = &DBRel{Type: RelOneToMany}
|
rel1 = &DBRel{Type: RelOneToMany}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rel1.Left.col = &c
|
||||||
rel1.Left.Table = t.Name
|
rel1.Left.Table = t.Name
|
||||||
rel1.Left.Col = c.Name
|
rel1.Left.Col = c.Name
|
||||||
rel1.Left.Array = c.Array
|
rel1.Left.Array = c.Array
|
||||||
|
|
||||||
|
rel1.Right.col = fc
|
||||||
rel1.Right.Table = c.FKeyTable
|
rel1.Right.Table = c.FKeyTable
|
||||||
rel1.Right.Col = fc.Name
|
rel1.Right.Col = fc.Name
|
||||||
rel1.Right.Array = fc.Array
|
rel1.Right.Array = fc.Array
|
||||||
|
|
114
psql/update.go
114
psql/update.go
|
@ -1,6 +1,8 @@
|
||||||
|
//nolint:errcheck
|
||||||
package psql
|
package psql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
@ -11,7 +13,7 @@ import (
|
||||||
func (c *compilerContext) renderUpdate(qc *qcode.QCode, w io.Writer,
|
func (c *compilerContext) renderUpdate(qc *qcode.QCode, w io.Writer,
|
||||||
vars Variables, ti *DBTableInfo) (uint32, error) {
|
vars Variables, ti *DBTableInfo) (uint32, error) {
|
||||||
|
|
||||||
insert, ok := vars[qc.ActionVar]
|
update, ok := vars[qc.ActionVar]
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("Variable '%s' not !defined", qc.ActionVar)
|
return 0, fmt.Errorf("Variable '%s' not !defined", qc.ActionVar)
|
||||||
}
|
}
|
||||||
|
@ -21,12 +23,15 @@ func (c *compilerContext) renderUpdate(qc *qcode.QCode, w io.Writer,
|
||||||
io.WriteString(c.w, `}}' :: json AS j)`)
|
io.WriteString(c.w, `}}' :: json AS j)`)
|
||||||
|
|
||||||
st := util.NewStack()
|
st := util.NewStack()
|
||||||
st.Push(kvitem{_type: itemUpdate, key: ti.Name, val: insert, ti: ti})
|
st.Push(kvitem{_type: itemUpdate, key: ti.Name, val: update, ti: ti})
|
||||||
|
|
||||||
for {
|
for {
|
||||||
if st.Len() == 0 {
|
if st.Len() == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
if update[0] == '[' && st.Len() > 1 {
|
||||||
|
return 0, errors.New("Nested bulk update not supported")
|
||||||
|
}
|
||||||
intf := st.Pop()
|
intf := st.Pop()
|
||||||
|
|
||||||
switch item := intf.(type) {
|
switch item := intf.(type) {
|
||||||
|
@ -45,12 +50,12 @@ func (c *compilerContext) renderUpdate(qc *qcode.QCode, w io.Writer,
|
||||||
switch item._type {
|
switch item._type {
|
||||||
case itemUpdate:
|
case itemUpdate:
|
||||||
err = c.renderUpdateStmt(w, qc, item)
|
err = c.renderUpdateStmt(w, qc, item)
|
||||||
// case itemConnect:
|
case itemConnect:
|
||||||
// err = c.renderConnectStmt(qc, w, item)
|
err = c.renderConnectStmt(qc, w, item)
|
||||||
// case itemDisconnect:
|
case itemDisconnect:
|
||||||
// err = c.renderDisconnectStmt(qc, w, item)
|
err = c.renderDisconnectStmt(qc, w, item)
|
||||||
case itemUnion:
|
case itemUnion:
|
||||||
err = c.renderUpdateUnionStmt(w, item)
|
err = c.renderUnionStmt(w, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -67,6 +72,7 @@ func (c *compilerContext) renderUpdate(qc *qcode.QCode, w io.Writer,
|
||||||
func (c *compilerContext) renderUpdateStmt(w io.Writer, qc *qcode.QCode, item renitem) error {
|
func (c *compilerContext) renderUpdateStmt(w io.Writer, qc *qcode.QCode, item renitem) error {
|
||||||
ti := item.ti
|
ti := item.ti
|
||||||
jt := item.data
|
jt := item.data
|
||||||
|
sk := nestedUpdateRelColumnsMap(item.kvitem)
|
||||||
|
|
||||||
io.WriteString(c.w, `, `)
|
io.WriteString(c.w, `, `)
|
||||||
renderCteName(c.w, item.kvitem)
|
renderCteName(c.w, item.kvitem)
|
||||||
|
@ -75,12 +81,15 @@ func (c *compilerContext) renderUpdateStmt(w io.Writer, qc *qcode.QCode, item re
|
||||||
io.WriteString(w, `UPDATE `)
|
io.WriteString(w, `UPDATE `)
|
||||||
quoted(w, ti.Name)
|
quoted(w, ti.Name)
|
||||||
io.WriteString(w, ` SET (`)
|
io.WriteString(w, ` SET (`)
|
||||||
renderInsertUpdateColumns(w, qc, jt, ti, nil, false)
|
renderInsertUpdateColumns(w, qc, jt, ti, sk, false)
|
||||||
|
renderNestedUpdateRelColumns(w, item.kvitem, false)
|
||||||
|
|
||||||
io.WriteString(w, `) = (SELECT `)
|
io.WriteString(w, `) = (SELECT `)
|
||||||
renderInsertUpdateColumns(w, qc, jt, ti, nil, true)
|
renderInsertUpdateColumns(w, qc, jt, ti, sk, true)
|
||||||
|
renderNestedUpdateRelColumns(w, item.kvitem, true)
|
||||||
|
|
||||||
io.WriteString(w, ` FROM "_sg_input" i, `)
|
io.WriteString(w, ` FROM "_sg_input" i, `)
|
||||||
|
renderNestedUpdateRelTables(w, item.kvitem)
|
||||||
|
|
||||||
if item.array {
|
if item.array {
|
||||||
io.WriteString(w, `json_populate_recordset`)
|
io.WriteString(w, `json_populate_recordset`)
|
||||||
|
@ -90,15 +99,24 @@ func (c *compilerContext) renderUpdateStmt(w io.Writer, qc *qcode.QCode, item re
|
||||||
|
|
||||||
io.WriteString(w, `(NULL::`)
|
io.WriteString(w, `(NULL::`)
|
||||||
io.WriteString(w, ti.Name)
|
io.WriteString(w, ti.Name)
|
||||||
io.WriteString(w, `, i.j) t`)
|
|
||||||
|
|
||||||
io.WriteString(w, ` WHERE `)
|
if len(item.path) == 0 {
|
||||||
|
io.WriteString(w, `, i.j) t)`)
|
||||||
|
} else {
|
||||||
|
io.WriteString(w, `, i.j->`)
|
||||||
|
joinPath(w, item.path)
|
||||||
|
io.WriteString(w, `) t) `)
|
||||||
|
}
|
||||||
|
|
||||||
if item.id != 0 {
|
if item.id != 0 {
|
||||||
// Render sql to set id values if child-to-parent
|
// Render sql to set id values if child-to-parent
|
||||||
// relationship is one-to-one
|
// relationship is one-to-one
|
||||||
rel := item.relCP
|
rel := item.relCP
|
||||||
io.WriteString(w, `((`)
|
|
||||||
|
io.WriteString(w, `FROM `)
|
||||||
|
quoted(w, rel.Right.Table)
|
||||||
|
|
||||||
|
io.WriteString(w, ` WHERE ((`)
|
||||||
colWithTable(w, rel.Left.Table, rel.Left.Col)
|
colWithTable(w, rel.Left.Table, rel.Left.Col)
|
||||||
io.WriteString(w, `) = (`)
|
io.WriteString(w, `) = (`)
|
||||||
colWithTable(w, rel.Right.Table, rel.Right.Col)
|
colWithTable(w, rel.Right.Table, rel.Right.Col)
|
||||||
|
@ -107,53 +125,68 @@ func (c *compilerContext) renderUpdateStmt(w io.Writer, qc *qcode.QCode, item re
|
||||||
if item.relPC.Type == RelOneToMany {
|
if item.relPC.Type == RelOneToMany {
|
||||||
if conn, ok := item.data["where"]; ok {
|
if conn, ok := item.data["where"]; ok {
|
||||||
io.WriteString(w, ` AND `)
|
io.WriteString(w, ` AND `)
|
||||||
renderWhereFromJSON(w, conn)
|
renderWhereFromJSON(w, item.ti.Name, conn)
|
||||||
} else if conn, ok := item.data["_where"]; ok {
|
} else if conn, ok := item.data["_where"]; ok {
|
||||||
io.WriteString(w, ` AND `)
|
io.WriteString(w, ` AND `)
|
||||||
renderWhereFromJSON(w, conn)
|
renderWhereFromJSON(w, item.ti.Name, conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
io.WriteString(w, `)`)
|
io.WriteString(w, `)`)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
io.WriteString(w, `WHERE `)
|
||||||
if err := c.renderWhere(&qc.Selects[0], ti); err != nil {
|
if err := c.renderWhere(&qc.Selects[0], ti); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
io.WriteString(w, `) RETURNING *)`)
|
io.WriteString(w, ` RETURNING `)
|
||||||
|
quoted(w, ti.Name)
|
||||||
|
io.WriteString(w, `.*)`)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderUpdateUnionStmt(w io.Writer, item renitem) error {
|
func nestedUpdateRelColumnsMap(item kvitem) map[string]struct{} {
|
||||||
renderCteName(w, item.kvitem)
|
sk := make(map[string]struct{}, len(item.items))
|
||||||
io.WriteString(w, ` AS (`)
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
for _, v := range item.items {
|
for _, v := range item.items {
|
||||||
if v._type == itemConnect {
|
//fmt.Println(">>", v._ctype > 0 && v.relCP.Type == RelOneToMany, v.relCP.Right.Col)
|
||||||
if i == 0 {
|
|
||||||
io.WriteString(w, `UPDATE `)
|
if v._ctype > 0 && v.relCP.Type == RelOneToMany {
|
||||||
quoted(w, v.ti.Name)
|
sk[v.relCP.Right.Col] = struct{}{}
|
||||||
io.WriteString(w, ` SET `)
|
}
|
||||||
quoted(w, v.relPC.Right.Col)
|
}
|
||||||
io.WriteString(w, ` = `)
|
|
||||||
colWithTable(w, v.relPC.Left.Table, v.relPC.Left.Col)
|
return sk
|
||||||
io.WriteString(w, ` WHERE `)
|
}
|
||||||
|
|
||||||
|
func renderNestedUpdateRelColumns(w io.Writer, item kvitem, values bool) error {
|
||||||
|
// Render child foreign key columns if child-to-parent
|
||||||
|
// relationship is one-to-many
|
||||||
|
for _, v := range item.items {
|
||||||
|
if v._ctype > 0 && v.relCP.Type == RelOneToMany {
|
||||||
|
io.WriteString(w, `, `)
|
||||||
|
if values {
|
||||||
|
colWithTable(w, v.relCP.Left.Table, v.relCP.Left.Col)
|
||||||
} else {
|
} else {
|
||||||
io.WriteString(w, ` OR (`)
|
quoted(w, v.relCP.Right.Col)
|
||||||
}
|
}
|
||||||
if err := renderKVItemWhere(w, v); err != nil {
|
}
|
||||||
return err
|
}
|
||||||
}
|
|
||||||
if i != 0 {
|
return nil
|
||||||
io.WriteString(w, `)`)
|
}
|
||||||
}
|
|
||||||
i++
|
func renderNestedUpdateRelTables(w io.Writer, item kvitem) error {
|
||||||
|
// Render child foreign key columns if child-to-parent
|
||||||
|
// relationship is one-to-many
|
||||||
|
for _, v := range item.items {
|
||||||
|
if v._ctype > 0 && v.relCP.Type == RelOneToMany {
|
||||||
|
quoted(w, v.relCP.Left.Table)
|
||||||
|
io.WriteString(w, `, `)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
io.WriteString(w, `)`)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -173,7 +206,8 @@ func (c *compilerContext) renderDelete(qc *qcode.QCode, w io.Writer,
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
io.WriteString(c.w, ` RETURNING *) `)
|
io.WriteString(w, ` RETURNING `)
|
||||||
|
quoted(w, ti.Name)
|
||||||
|
io.WriteString(w, `.*)`)
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package psql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -14,7 +13,7 @@ func singleUpdate(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{update}}' :: json AS j), "products" AS (UPDATE "products" SET ("name", "description") = (SELECT "t"."name", "t"."description" FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t WHERE (("products"."id") = 1) AND (("products"."id") = 15)) RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{update}}' :: json AS j), "products" AS (UPDATE "products" SET ("name", "description") = (SELECT "t"."name", "t"."description" FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t)WHERE (("products"."id") = 1) AND (("products"."id") = 15) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"update": json.RawMessage(` { "name": "my_name", "description": "my_desc" }`),
|
"update": json.RawMessage(` { "name": "my_name", "description": "my_desc" }`),
|
||||||
|
@ -37,7 +36,7 @@ func simpleUpdateWithPresets(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "products" AS (UPDATE "products" SET ("name", "price", "updated_at") = (SELECT "t"."name", "t"."price", 'now' :: timestamp without time zone FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t WHERE (("products"."user_id") = '{{user_id}}' :: bigint)) RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "products" AS (UPDATE "products" SET ("name", "price", "updated_at") = (SELECT "t"."name", "t"."price", 'now' :: timestamp without time zone FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t)WHERE (("products"."user_id") = '{{user_id}}' :: bigint) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{"name": "Apple", "price": 1.25}`),
|
"data": json.RawMessage(`{"name": "Apple", "price": 1.25}`),
|
||||||
|
@ -72,9 +71,9 @@ func nestedUpdateManyToMany(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql1 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "purchases" AS (UPDATE "purchases" SET ("sale_type", "quantity", "due_date") = (SELECT "t"."sale_type", "t"."quantity", "t"."due_date" FROM "_sg_input" i, json_populate_record(NULL::purchases, i.j) t WHERE (("purchases"."id") = 5)) RETURNING *), "customers" AS (UPDATE "customers" SET ("full_name", "email") = (SELECT "t"."full_name", "t"."email" FROM "_sg_input" i, json_populate_record(NULL::customers, i.j) t WHERE (("customers"."id") = ("purchases"."customer_id"))) RETURNING *), "products" AS (UPDATE "products" SET ("name", "price") = (SELECT "t"."name", "t"."price" FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t WHERE (("products"."id") = ("purchases"."product_id"))) RETURNING *) SELECT json_object_agg('purchase', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "purchases_0"."sale_type" AS "sale_type", "purchases_0"."quantity" AS "quantity", "purchases_0"."due_date" AS "due_date", "product_1_join"."json_1" AS "product", "customer_2_join"."json_2" AS "customer") AS "json_row_0")) AS "json_0" FROM (SELECT "purchases"."sale_type", "purchases"."quantity", "purchases"."due_date", "purchases"."product_id", "purchases"."customer_id" FROM "purchases" LIMIT ('1') :: integer) AS "purchases_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_2" FROM (SELECT "customers_2"."id" AS "id", "customers_2"."full_name" AS "full_name", "customers_2"."email" AS "email") AS "json_row_2")) AS "json_2" FROM (SELECT "customers"."id", "customers"."full_name", "customers"."email" FROM "customers" WHERE ((("customers"."id") = ("purchases_0"."customer_id"))) LIMIT ('1') :: integer) AS "customers_2" LIMIT ('1') :: integer) AS "customer_2_join" ON ('true') LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."id") = ("purchases_0"."product_id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql1 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "purchases" AS (UPDATE "purchases" SET ("sale_type", "quantity", "due_date") = (SELECT "t"."sale_type", "t"."quantity", "t"."due_date" FROM "_sg_input" i, json_populate_record(NULL::purchases, i.j) t)WHERE (("purchases"."id") = 5) RETURNING "purchases".*), "products" AS (UPDATE "products" SET ("name", "price") = (SELECT "t"."name", "t"."price" FROM "_sg_input" i, json_populate_record(NULL::products, i.j->'product') t) FROM "purchases" WHERE (("products"."id") = ("purchases"."product_id")) RETURNING "products".*), "customers" AS (UPDATE "customers" SET ("full_name", "email") = (SELECT "t"."full_name", "t"."email" FROM "_sg_input" i, json_populate_record(NULL::customers, i.j->'customer') t) FROM "purchases" WHERE (("customers"."id") = ("purchases"."customer_id")) RETURNING "customers".*) SELECT json_object_agg('purchase', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "purchases_0"."sale_type" AS "sale_type", "purchases_0"."quantity" AS "quantity", "purchases_0"."due_date" AS "due_date", "product_1_join"."json_1" AS "product", "customer_2_join"."json_2" AS "customer") AS "json_row_0")) AS "json_0" FROM (SELECT "purchases"."sale_type", "purchases"."quantity", "purchases"."due_date", "purchases"."product_id", "purchases"."customer_id" FROM "purchases" LIMIT ('1') :: integer) AS "purchases_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_2" FROM (SELECT "customers_2"."id" AS "id", "customers_2"."full_name" AS "full_name", "customers_2"."email" AS "email") AS "json_row_2")) AS "json_2" FROM (SELECT "customers"."id", "customers"."full_name", "customers"."email" FROM "customers" WHERE ((("customers"."id") = ("purchases_0"."customer_id"))) LIMIT ('1') :: integer) AS "customers_2" LIMIT ('1') :: integer) AS "customer_2_join" ON ('true') LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."id") = ("purchases_0"."product_id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
sql2 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "purchases" AS (UPDATE "purchases" SET ("sale_type", "quantity", "due_date") = (SELECT "t"."sale_type", "t"."quantity", "t"."due_date" FROM "_sg_input" i, json_populate_record(NULL::purchases, i.j) t WHERE (("purchases"."id") = 5)) RETURNING *), "products" AS (UPDATE "products" SET ("name", "price") = (SELECT "t"."name", "t"."price" FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t WHERE (("products"."id") = ("purchases"."product_id"))) RETURNING *), "customers" AS (UPDATE "customers" SET ("full_name", "email") = (SELECT "t"."full_name", "t"."email" FROM "_sg_input" i, json_populate_record(NULL::customers, i.j) t WHERE (("customers"."id") = ("purchases"."customer_id"))) RETURNING *) SELECT json_object_agg('purchase', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "purchases_0"."sale_type" AS "sale_type", "purchases_0"."quantity" AS "quantity", "purchases_0"."due_date" AS "due_date", "product_1_join"."json_1" AS "product", "customer_2_join"."json_2" AS "customer") AS "json_row_0")) AS "json_0" FROM (SELECT "purchases"."sale_type", "purchases"."quantity", "purchases"."due_date", "purchases"."product_id", "purchases"."customer_id" FROM "purchases" LIMIT ('1') :: integer) AS "purchases_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_2" FROM (SELECT "customers_2"."id" AS "id", "customers_2"."full_name" AS "full_name", "customers_2"."email" AS "email") AS "json_row_2")) AS "json_2" FROM (SELECT "customers"."id", "customers"."full_name", "customers"."email" FROM "customers" WHERE ((("customers"."id") = ("purchases_0"."customer_id"))) LIMIT ('1') :: integer) AS "customers_2" LIMIT ('1') :: integer) AS "customer_2_join" ON ('true') LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."id") = ("purchases_0"."product_id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql2 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "purchases" AS (UPDATE "purchases" SET ("sale_type", "quantity", "due_date") = (SELECT "t"."sale_type", "t"."quantity", "t"."due_date" FROM "_sg_input" i, json_populate_record(NULL::purchases, i.j) t)WHERE (("purchases"."id") = 5) RETURNING "purchases".*), "customers" AS (UPDATE "customers" SET ("full_name", "email") = (SELECT "t"."full_name", "t"."email" FROM "_sg_input" i, json_populate_record(NULL::customers, i.j->'customer') t) FROM "purchases" WHERE (("customers"."id") = ("purchases"."customer_id")) RETURNING "customers".*), "products" AS (UPDATE "products" SET ("name", "price") = (SELECT "t"."name", "t"."price" FROM "_sg_input" i, json_populate_record(NULL::products, i.j->'product') t) FROM "purchases" WHERE (("products"."id") = ("purchases"."product_id")) RETURNING "products".*) SELECT json_object_agg('purchase', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "purchases_0"."sale_type" AS "sale_type", "purchases_0"."quantity" AS "quantity", "purchases_0"."due_date" AS "due_date", "product_1_join"."json_1" AS "product", "customer_2_join"."json_2" AS "customer") AS "json_row_0")) AS "json_0" FROM (SELECT "purchases"."sale_type", "purchases"."quantity", "purchases"."due_date", "purchases"."product_id", "purchases"."customer_id" FROM "purchases" LIMIT ('1') :: integer) AS "purchases_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_2" FROM (SELECT "customers_2"."id" AS "id", "customers_2"."full_name" AS "full_name", "customers_2"."email" AS "email") AS "json_row_2")) AS "json_2" FROM (SELECT "customers"."id", "customers"."full_name", "customers"."email" FROM "customers" WHERE ((("customers"."id") = ("purchases_0"."customer_id"))) LIMIT ('1') :: integer) AS "customers_2" LIMIT ('1') :: integer) AS "customer_2_join" ON ('true') LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."id") = ("purchases_0"."product_id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(` {
|
"data": json.RawMessage(` {
|
||||||
|
@ -120,7 +119,7 @@ func nestedUpdateOneToMany(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t WHERE (("users"."id") = 8)) RETURNING *), "products" AS (UPDATE "products" SET ("name", "price", "created_at", "updated_at") = (SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t WHERE (("products"."user_id") = ("users"."id") AND "id" = '2')) RETURNING *) SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t)WHERE (("users"."id") = 8) RETURNING "users".*), "products" AS (UPDATE "products" SET ("name", "price", "created_at", "updated_at") = (SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::products, i.j->'product') t) FROM "users" WHERE (("products"."user_id") = ("users"."id") AND "products"."id" = '2') RETURNING "products".*) SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
|
@ -163,7 +162,7 @@ func nestedUpdateOneToOne(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "products" AS (UPDATE "products" SET ("name", "price", "created_at", "updated_at") = (SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t WHERE (("products"."id") = 6)) RETURNING *), "users" AS (UPDATE "users" SET ("email") = (SELECT "t"."email" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t WHERE (("users"."id") = ("products"."user_id"))) RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "products" AS (UPDATE "products" SET ("name", "price", "created_at", "updated_at") = (SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::products, i.j) t)WHERE (("products"."id") = 6) RETURNING "products".*), "users" AS (UPDATE "users" SET ("email") = (SELECT "t"."email" FROM "_sg_input" i, json_populate_record(NULL::users, i.j->'user') t) FROM "products" WHERE (("users"."id") = ("products"."user_id")) RETURNING "users".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
|
@ -201,9 +200,7 @@ func nestedUpdateOneToManyWithConnect(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql1 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t WHERE (("users"."id") = 6)) RETURNING *), "products_3" AS (UPDATE "products" SET "user_id" = NULL WHERE "products"."user_id" = "users"."id" AND "id" = '8' RETURNING *), "products_2" AS (UPDATE "products" SET "user_id" = "users"."id" WHERE "id" = '7' RETURNING *), "products" AS (SELECT * FROM "products_2" UNION ALL SELECT * FROM "products_3") SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql1 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t)WHERE (("users"."id") = 6) RETURNING "users".*), "products_c" AS ( UPDATE "products" SET "user_id" = "users"."id"FROM "users" WHERE ("products"."id" = '7') RETURNING "products".*), "products_d" AS ( UPDATE "products" SET "user_id" = NULL FROM "users" WHERE ("products"."id" = '8') RETURNING "products".*), "products" AS (SELECT * FROM "products_c" UNION ALL SELECT * FROM "products_d") SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
sql2 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t WHERE (("users"."id") = 6)) RETURNING *), "products_3" AS (UPDATE "products" SET "user_id" = "users"."id" WHERE "id" = '7' RETURNING *), "products_2" AS (UPDATE "products" SET "user_id" = NULL WHERE "products"."user_id" = "users"."id" AND "id" = '8' RETURNING *), "products" AS (SELECT * FROM "products_2" UNION ALL SELECT * FROM "products_3") SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
|
@ -218,17 +215,15 @@ func nestedUpdateOneToManyWithConnect(t *testing.T) {
|
||||||
}`),
|
}`),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < 1000; i++ {
|
|
||||||
resSQL, err := compileGQLToPSQL(gql, vars, "admin")
|
resSQL, err := compileGQLToPSQL(gql, vars, "admin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(resSQL) != sql1 && string(resSQL) != sql2 {
|
if string(resSQL) != sql1 {
|
||||||
t.Fatal(errNotExpected)
|
t.Fatal(errNotExpected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func nestedUpdateOneToOneWithConnect(t *testing.T) {
|
func nestedUpdateOneToOneWithConnect(t *testing.T) {
|
||||||
gql := `mutation {
|
gql := `mutation {
|
||||||
|
@ -243,14 +238,12 @@ func nestedUpdateOneToOneWithConnect(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT * FROM "users" WHERE "id" = '5' AND "email" = 'test@test.com' LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "created_at", "updated_at", "user_id") = (SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t WHERE (("products"."id") = 9)) RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT * FROM "users" WHERE "users"."id" = '5' AND "users"."email" = 'test@test.com' LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT "t"."name", "t"."price", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t)WHERE (("products"."id") = 9) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
"name": "Apple",
|
"name": "Apple",
|
||||||
"price": 1.25,
|
"price": 1.25,
|
||||||
"created_at": "now",
|
|
||||||
"updated_at": "now",
|
|
||||||
"user": {
|
"user": {
|
||||||
"connect": { "id": 5, "email": "test@test.com" }
|
"connect": { "id": 5, "email": "test@test.com" }
|
||||||
}
|
}
|
||||||
|
@ -261,7 +254,37 @@ func nestedUpdateOneToOneWithConnect(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
fmt.Println(string(resSQL))
|
|
||||||
|
if string(resSQL) != sql {
|
||||||
|
t.Fatal(errNotExpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nestedUpdateOneToOneWithDisconnect(t *testing.T) {
|
||||||
|
gql := `mutation {
|
||||||
|
product(update: $data, id: 2) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user_id
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT * FROM (VALUES(NULL::bigint)) AS LOOKUP("id")), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT "t"."name", "t"."price", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t)WHERE (("products"."id") = 2) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "products_0"."user_id" AS "user_id") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
|
vars := map[string]json.RawMessage{
|
||||||
|
"data": json.RawMessage(`{
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"user": {
|
||||||
|
"disconnect": { "id": 5 }
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
|
||||||
|
resSQL, err := compileGQLToPSQL(gql, vars, "admin")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
if string(resSQL) != sql {
|
if string(resSQL) != sql {
|
||||||
t.Fatal(errNotExpected)
|
t.Fatal(errNotExpected)
|
||||||
|
@ -276,4 +299,5 @@ func TestCompileUpdate(t *testing.T) {
|
||||||
t.Run("nestedUpdateOneToOne", nestedUpdateOneToOne)
|
t.Run("nestedUpdateOneToOne", nestedUpdateOneToOne)
|
||||||
t.Run("nestedUpdateOneToManyWithConnect", nestedUpdateOneToManyWithConnect)
|
t.Run("nestedUpdateOneToManyWithConnect", nestedUpdateOneToManyWithConnect)
|
||||||
t.Run("nestedUpdateOneToOneWithConnect", nestedUpdateOneToOneWithConnect)
|
t.Run("nestedUpdateOneToOneWithConnect", nestedUpdateOneToOneWithConnect)
|
||||||
|
t.Run("nestedUpdateOneToOneWithDisconnect", nestedUpdateOneToOneWithDisconnect)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue