Compare commits

...

13 Commits

Author SHA1 Message Date
7acf28bb3c Fix issue with upgrading to postgres 12 docker image #36 2020-02-24 02:37:21 +05:30
be5d4e976a Misprint (#41) 2020-02-24 02:04:23 +05:30
d1b884aec6 Misprint (#40) 2020-02-24 02:03:57 +05:30
4be4ce860b Misprint (#39) 2020-02-24 02:03:40 +05:30
dfa4caf540 Misprint (#37) 2020-02-24 02:03:27 +05:30
7763251fb7 fix "Try the demo app" in docs (#38)
* fix "Try the demo app" in docs

* fix "Get Started" setup in docs
2020-02-24 02:02:22 +05:30
51e105699e Fix corrupt json bug in jsn package 2020-02-24 02:00:11 +05:30
90694f8803 Fix spelling in docs (#34) 2020-02-23 15:41:04 +05:30
ad82f5b267 Fix spelling in docs (#35) 2020-02-23 15:40:42 +05:30
99b37a9c50 Fix bug related to new Postgres docker image 2020-02-23 10:28:32 +05:30
7ec1f59224 Fix bug with cursors and multiple order by 2020-02-23 02:28:37 +05:30
d3ecb1d6cc Fix bug with multi root queries 2020-02-21 10:29:37 +05:30
aed4170e8e Fix bug with cursor filters 2020-02-20 22:53:29 +05:30
25 changed files with 538 additions and 463 deletions

View File

@ -35,7 +35,7 @@ $(GORICE):
$(WEB_BUILD_DIR): $(WEB_BUILD_DIR):
@echo "First install Yarn and create a build of the web UI found under ./web" @echo "First install Yarn and create a build of the web UI found under ./web"
@echo "Command: cd web && yarn build" @echo "Command: cd web && yarn && yarn build"
@exit 1 @exit 1
$(GITCHGLOG): $(GITCHGLOG):
@ -77,7 +77,7 @@ clean:
run: clean run: clean
@go run $(BUILD_FLAGS) main.go $(ARGS) @go run $(BUILD_FLAGS) main.go $(ARGS)
install: install: gen
@echo $(GOPATH) @echo $(GOPATH)
@echo "Commit Hash: `git rev-parse HEAD`" @echo "Commit Hash: `git rev-parse HEAD`"
@echo "Old Hash: `shasum $(GOPATH)/bin/$(BINARY) 2>/dev/null | cut -c -32`" @echo "Old Hash: `shasum $(GOPATH)/bin/$(BINARY) 2>/dev/null | cut -c -32`"

View File

@ -93,7 +93,7 @@ database:
port: 5432 port: 5432
dbname: app_development dbname: app_development
user: postgres user: postgres
password: '' password: postgres
#schema: "public" #schema: "public"
#pool_size: 10 #pool_size: 10

View File

@ -54,7 +54,7 @@ database:
port: 5432 port: 5432
dbname: app_production dbname: app_production
user: postgres user: postgres
password: '' password: postgres
#pool_size: 10 #pool_size: 10
#max_retries: 0 #max_retries: 0
#log_level: "debug" #log_level: "debug"

View File

@ -1,7 +1,10 @@
version: '3.4' version: '3.4'
services: services:
db: db:
image: postgres image: postgres:12
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports: ports:
- "5432:5432" - "5432:5432"

View File

@ -34,6 +34,12 @@ Super Graph has a rich feature set like integrating with your existing Ruby on R
# clone the repository # clone the repository
git clone https://github.com/dosco/super-graph git clone https://github.com/dosco/super-graph
# run db in background
docker-compose up -d db
# see logs and wait until DB is really UP
docker-compose logs db
# setup the demo rails app & database and run it # setup the demo rails app & database and run it
docker-compose run rails_app rake db:create db:migrate db:seed docker-compose run rails_app rake db:create db:migrate db:seed
@ -137,7 +143,7 @@ What if I told you Super Graph will fetch all this data with a single SQL query
```graphql ```graphql
query { query {
products(limit 5, where: { price: { gt: 12 } }) { products(limit: 5, where: { price: { gt: 12 } }) {
id id
name name
description description
@ -153,7 +159,7 @@ query {
} }
} }
purchases( purchases(
limit 10, limit: 10,
order_by: { created_at: desc } , order_by: { created_at: desc } ,
where: { user_id: { eq: $user_id } } where: { user_id: { eq: $user_id } }
) { ) {
@ -216,7 +222,7 @@ You can then add your database schema to the migrations, maybe create some seed
git clone https://github.com/dosco/super-graph && cd super-graph && make install git clone https://github.com/dosco/super-graph && cd super-graph && make install
``` ```
And then create and launch you're new app And then create and launch your new app
```bash ```bash
# create a new app and change to it's directory # create a new app and change to it's directory
@ -1133,7 +1139,7 @@ query {
## Using Variables ## 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 built-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.
```javascript ```javascript
@ -1552,7 +1558,7 @@ roles:
This configuration is relatively simple to follow the `roles_query` parameter is the query that must be run to help figure out a users role. This query can be as complex as you like and include joins with other tables. This configuration is relatively simple to follow the `roles_query` parameter is the query that must be run to help figure out a users role. This query can be as complex as you like and include joins with other tables.
The individual roles are defined under the `roles` parameter and this includes each table the role has a custom setting for. The role is dynamically matched using the `match` parameter for example in the above case `users.id = 1` means that when the `roles_query` is executed a user with the id `1` willbe assigned the admin role and those that don't match get the `user` role if authenticated successfully or the `anon` role. The individual roles are defined under the `roles` parameter and this includes each table the role has a custom setting for. The role is dynamically matched using the `match` parameter for example in the above case `users.id = 1` means that when the `roles_query` is executed a user with the id `1` will be assigned the admin role and those that don't match get the `user` role if authenticated successfully or the `anon` role.
## Remote Joins ## Remote Joins
@ -1767,7 +1773,7 @@ database:
port: 5432 port: 5432
dbname: app_development dbname: app_development
user: postgres user: postgres
password: '' password: postgres
#schema: "public" #schema: "public"
#pool_size: 10 #pool_size: 10

View File

@ -13,7 +13,7 @@ Super Graph code is made up of a number of packages. We have done our best to ke
## QCODE ## QCODE
This package contains the core of the GraphQL conpiler it handling the lexing and parsing of the GraphQL query transforming it into an internal representation called This package contains the core of the GraphQL compiler it handling the lexing and parsing of the GraphQL query transforming it into an internal representation called
`QCode`. `QCode`.
This is the first step of the compiling process the `func NewCompiler(c Config)` function creates a new instance of this compiler which has it's own config. This is the first step of the compiling process the `func NewCompiler(c Config)` function creates a new instance of this compiler which has it's own config.
@ -71,7 +71,7 @@ item{itemObjOpen, 16, 20} // {
... ...
``` ```
These tokens are then fed into the parser `parse.go` the parser does the work of generating an abstract syntax tree (AST) from the tokens. This AST is an internal representation (data structure) and is not exposed outside the package. Sinc the AST is a tree a stack `stack.go` is used to walk the tree and generate the QCode AST. The QCode data structure is also a tree (represented as an array). This is then returned to the caller of the compile function. These tokens are then fed into the parser `parse.go` the parser does the work of generating an abstract syntax tree (AST) from the tokens. This AST is an internal representation (data structure) and is not exposed outside the package. Since the AST is a tree a stack `stack.go` is used to walk the tree and generate the QCode AST. The QCode data structure is also a tree (represented as an array). This is then returned to the caller of the compile function.
```go ```go
type Operation struct { type Operation struct {
@ -238,4 +238,4 @@ ok github.com/dosco/super-graph/psql 2.530s
## Reach out ## Reach out
If you'd like me to explain other parts of the code please reach out over Twitter or Discord. I'll keep adding to this doc as I get time. If you'd like me to explain other parts of the code please reach out over Twitter or Discord. I'll keep adding to this doc as I get time.

View File

@ -19,7 +19,7 @@ default: &default
encoding: unicode encoding: unicode
host: db host: db
username: postgres username: postgres
password: password: postgres
pool: 5 pool: 5
development: development:

View File

@ -27,14 +27,20 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error {
var k []byte var k []byte
state := expectKey state := expectKey
instr := false
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
if state == expectObjClose || state == expectListClose { if state == expectObjClose || state == expectListClose {
switch b[i] { if b[i-1] != '\\' && b[i] == '"' {
case '{', '[': instr = !instr
d++ }
case '}', ']': if !instr {
d-- switch b[i] {
case '{', '[':
d++
case '}', ']':
d--
}
} }
} }

View File

@ -51,13 +51,20 @@ func Get(b []byte, keys [][]byte) []Field {
state := expectKey state := expectKey
n := 0 n := 0
instr := false
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
if state == expectObjClose || state == expectListClose { if state == expectObjClose || state == expectListClose {
switch b[i] { if b[i-1] != '\\' && b[i] == '"' {
case '{', '[': instr = !instr
d++ }
case '}', ']': if !instr {
d-- switch b[i] {
case '{', '[':
d++
case '}', ']':
d--
}
} }
} }

View File

@ -2,6 +2,7 @@ package jsn
import ( import (
"bytes" "bytes"
"io/ioutil"
"testing" "testing"
) )
@ -161,6 +162,8 @@ var (
input6 = ` input6 = `
{"users" : [{"id" : 1, "email" : "vicram@gmail.com", "slug" : "vikram-rangnekar", "threads" : [], "threads_cursor" : null}, {"id" : 3, "email" : "marareilly@lang.name", "slug" : "raymundo-corwin", "threads" : [{"id" : 9, "title" : "Et alias et aut porro praesentium nam in voluptatem reiciendis quisquam perspiciatis inventore eos quia et et enim qui amet."}, {"id" : 25, "title" : "Ipsam quam nemo culpa tempore amet optio sit sed eligendi autem consequatur quaerat rem velit quibusdam quibusdam optio a voluptatem."}], "threads_cursor" : 25}], "users_cursor" : 3}` {"users" : [{"id" : 1, "email" : "vicram@gmail.com", "slug" : "vikram-rangnekar", "threads" : [], "threads_cursor" : null}, {"id" : 3, "email" : "marareilly@lang.name", "slug" : "raymundo-corwin", "threads" : [{"id" : 9, "title" : "Et alias et aut porro praesentium nam in voluptatem reiciendis quisquam perspiciatis inventore eos quia et et enim qui amet."}, {"id" : 25, "title" : "Ipsam quam nemo culpa tempore amet optio sit sed eligendi autem consequatur quaerat rem velit quibusdam quibusdam optio a voluptatem."}], "threads_cursor" : 25}], "users_cursor" : 3}`
input7, _ = ioutil.ReadFile("test.json")
) )
func TestGet(t *testing.T) { func TestGet(t *testing.T) {
@ -256,6 +259,15 @@ func TestGet2(t *testing.T) {
} }
} }
func TestGet3(t *testing.T) {
values := Get(input7, [][]byte{[]byte("data")})
v := values[0].Value
if !bytes.Equal(v[len(v)-11:], []byte(`Rangnekar"}`)) {
t.Fatal("corrupt ending")
}
}
func TestValue(t *testing.T) { func TestValue(t *testing.T) {
v1 := []byte("12345") v1 := []byte("12345")
if !bytes.Equal(Value(v1), v1) { if !bytes.Equal(Value(v1), v1) {

View File

@ -10,15 +10,20 @@ func Keys(b []byte) [][]byte {
st := NewStack() st := NewStack()
ae := 0 ae := 0
instr := false
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
if state == expectObjClose || state == expectListClose { if state == expectObjClose || state == expectListClose {
switch b[i] { if b[i-1] != '\\' && b[i] == '"' {
case '{', '[': instr = !instr
d++ }
case '}', ']': if !instr {
d-- switch b[i] {
case '{', '[':
d++
case '}', ']':
d--
}
} }
} }

View File

@ -32,6 +32,8 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
state := expectKey state := expectKey
ws, we := -1, len(b) ws, we := -1, len(b)
instr := false
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
// skip any left padding whitespace // skip any left padding whitespace
if ws == -1 && (b[i] == '{' || b[i] == '[') { if ws == -1 && (b[i] == '{' || b[i] == '[') {
@ -39,11 +41,16 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
} }
if state == expectObjClose || state == expectListClose { if state == expectObjClose || state == expectListClose {
switch b[i] { if b[i-1] != '\\' && b[i] == '"' {
case '{', '[': instr = !instr
d++ }
case '}', ']': if !instr {
d-- switch b[i] {
case '{', '[':
d++
case '}', ']':
d--
}
} }
} }

View File

@ -11,14 +11,20 @@ func Strip(b []byte, path [][]byte) []byte {
pi := 0 pi := 0
pm := false pm := false
state := expectKey state := expectKey
instr := false
for i := 0; i < len(b); i++ { for i := 0; i < len(b); i++ {
if state == expectObjClose || state == expectListClose { if state == expectObjClose || state == expectListClose {
switch b[i] { if b[i-1] != '\\' && b[i] == '"' {
case '{', '[': instr = !instr
d++ }
case '}', ']': if !instr {
d-- switch b[i] {
case '{', '[':
d++
case '}', ']':
d--
}
} }
} }

1
jsn/test.json Normal file

File diff suppressed because one or more lines are too long

View File

@ -100,12 +100,7 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer, vars Variables) (
io.WriteString(c.w, `, `) io.WriteString(c.w, `, `)
} }
ti, err := c.schema.GetTable(root.Name) c.renderRootSelect(root)
if err != nil {
return 0, err
}
c.renderRootSelect(root, ti)
i++ i++
} }
@ -136,10 +131,10 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer, vars Variables) (
io.WriteString(c.w, `(`) io.WriteString(c.w, `(`)
} else { } else {
c.renderLateralJoin(sel) c.renderLateralJoin(sel)
}
if !ti.Singular { if !ti.Singular {
c.renderPluralSelect(sel, ti) c.renderPluralSelect(sel, ti)
}
} }
skipped, err := c.renderSelect(sel, ti, vars) skipped, err := c.renderSelect(sel, ti, vars)
@ -169,6 +164,11 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer, vars Variables) (
return 0, err return 0, err
} }
if !ti.Singular {
io.WriteString(c.w, `)`)
aliasWithID(c.w, "__sel", sel.ID)
}
if sel.ParentID == -1 { if sel.ParentID == -1 {
io.WriteString(c.w, `)`) io.WriteString(c.w, `)`)
aliasWithID(c.w, "__sel", sel.ID) aliasWithID(c.w, "__sel", sel.ID)
@ -177,10 +177,6 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer, vars Variables) (
io.WriteString(c.w, `, `) io.WriteString(c.w, `, `)
} }
} else { } else {
if !ti.Singular {
io.WriteString(c.w, `)`)
aliasWithID(c.w, "__sel", sel.ID)
}
c.renderLateralJoinClose(sel) c.renderLateralJoinClose(sel)
} }
@ -233,50 +229,25 @@ func (c *compilerContext) renderPluralSelect(sel *qcode.Select, ti *DBTableInfo)
return nil return nil
} }
func (c *compilerContext) renderRootSelect(sel *qcode.Select, ti *DBTableInfo) error { func (c *compilerContext) renderRootSelect(sel *qcode.Select) error {
io.WriteString(c.w, `'`) io.WriteString(c.w, `'`)
io.WriteString(c.w, sel.FieldName) io.WriteString(c.w, sel.FieldName)
io.WriteString(c.w, `', `) io.WriteString(c.w, `', `)
if ti.Singular { io.WriteString(c.w, `"__sel_`)
int2string(c.w, sel.ID)
io.WriteString(c.w, `"."json"`)
if sel.Paging.Type != qcode.PtOffset {
io.WriteString(c.w, `, '`)
io.WriteString(c.w, sel.FieldName)
io.WriteString(c.w, `_cursor', `)
io.WriteString(c.w, `"__sel_`) io.WriteString(c.w, `"__sel_`)
int2string(c.w, sel.ID) int2string(c.w, sel.ID)
io.WriteString(c.w, `"."json"`) io.WriteString(c.w, `"."cursor"`)
} else {
io.WriteString(c.w, `coalesce(json_agg("__sel_`)
int2string(c.w, sel.ID)
io.WriteString(c.w, `"."json"), '[]')`)
if sel.Paging.Type != qcode.PtOffset {
n := 0
// check if primary key already included in order by
// query argument
for _, ob := range sel.OrderBy {
if ob.Col == ti.PrimaryCol.Key {
n = 1
break
}
}
if n == 1 {
n = len(sel.OrderBy)
} else {
n = len(sel.OrderBy) + 1
}
io.WriteString(c.w, `, '`)
io.WriteString(c.w, sel.FieldName)
io.WriteString(c.w, `_cursor', CONCAT_WS(','`)
for i := 0; i < n; i++ {
io.WriteString(c.w, `, max("__cur_`)
int2string(c.w, int32(i))
io.WriteString(c.w, `")`)
}
io.WriteString(c.w, `)`)
}
} }
return nil return nil
} }
@ -296,58 +267,29 @@ func (c *compilerContext) initSelect(sel *qcode.Select, ti *DBTableInfo, vars Va
if sel.Paging.Type != qcode.PtOffset { if sel.Paging.Type != qcode.PtOffset {
colmap[ti.PrimaryCol.Key] = struct{}{} colmap[ti.PrimaryCol.Key] = struct{}{}
addPrimaryKey := true
addToOrderBy := true
for _, ob := range sel.OrderBy { for _, ob := range sel.OrderBy {
if ob.Col == ti.PrimaryCol.Key { if ob.Col == ti.PrimaryCol.Key {
addToOrderBy = false addPrimaryKey = false
} break
if sel.Paging.Cursor {
fil := qcode.AddFilter(sel)
fil.Col = ob.Col
fil.Type = qcode.ValRef
fil.Table = "__cur"
fil.Val = ob.Col
switch ob.Order {
case qcode.OrderAsc:
fil.Op = qcode.OpGreaterThan
case qcode.OrderDesc:
fil.Op = qcode.OpLesserThan
}
} }
} }
if addToOrderBy { if addPrimaryKey {
var op qcode.ExpOp ob := &qcode.OrderBy{Col: ti.PrimaryCol.Name, Order: qcode.OrderAsc}
ob := &qcode.OrderBy{Col: ti.PrimaryCol.Name} if sel.Paging.Type == qcode.PtBackward {
sel.OrderBy = append(sel.OrderBy, ob)
switch sel.Paging.Type {
case qcode.PtForward:
op = qcode.OpGreaterThan
ob.Order = qcode.OrderAsc
case qcode.PtBackward:
op = qcode.OpLesserThan
ob.Order = qcode.OrderDesc ob.Order = qcode.OrderDesc
} }
sel.OrderBy = append(sel.OrderBy, ob)
if sel.Paging.Cursor {
fil := qcode.AddFilter(sel)
fil.Op = op
fil.Col = ti.PrimaryCol.Name
fil.Type = qcode.ValRef
fil.Table = "__cur"
fil.Val = ti.PrimaryCol.Name
}
} }
} }
if sel.Paging.Cursor {
c.addSeekPredicate(sel)
}
for _, id := range sel.Children { for _, id := range sel.Children {
child := &c.s[id] child := &c.s[id]
@ -393,6 +335,72 @@ func (c *compilerContext) initSelect(sel *qcode.Select, ti *DBTableInfo, vars Va
return skipped, cols, nil return skipped, cols, nil
} }
// This
// (A, B, C) >= (X, Y, Z)
//
// Becomes
// (A > X)
// OR ((A = X) AND (B > Y))
// OR ((A = X) AND (B = Y) AND (C > Z))
// OR ((A = X) AND (B = Y) AND (C = Z))
func (c *compilerContext) addSeekPredicate(sel *qcode.Select) error {
var or, and *qcode.Exp
obLen := len(sel.OrderBy)
if obLen > 1 {
or = qcode.NewFilter()
or.Op = qcode.OpOr
}
for i := 0; i < obLen; i++ {
if i > 0 {
and = qcode.NewFilter()
and.Op = qcode.OpAnd
}
for n, ob := range sel.OrderBy {
f := qcode.NewFilter()
f.Col = ob.Col
f.Type = qcode.ValRef
f.Table = "__cur"
f.Val = ob.Col
if obLen == 1 {
qcode.AddFilter(sel, f)
return nil
}
switch {
case i > 0 && n != i:
f.Op = qcode.OpEquals
case ob.Order == qcode.OrderDesc:
f.Op = qcode.OpLesserThan
default:
f.Op = qcode.OpGreaterThan
}
if and != nil {
and.Children = append(and.Children, f)
} else {
or.Children = append(or.Children, f)
}
if n == i {
break
}
}
if and != nil {
or.Children = append(or.Children, and)
}
}
qcode.AddFilter(sel, or)
return nil
}
func (c *compilerContext) renderSelect(sel *qcode.Select, ti *DBTableInfo, vars Variables) (uint32, error) { func (c *compilerContext) renderSelect(sel *qcode.Select, ti *DBTableInfo, vars Variables) (uint32, error) {
var rel *DBRel var rel *DBRel
var err error var err error
@ -428,31 +436,6 @@ func (c *compilerContext) renderSelect(sel *qcode.Select, ti *DBTableInfo, vars
} }
} }
//if !ti.Singular {
// io.WriteString(c.w, `SELECT coalesce(json_agg(json_build_object(`)
// if err := c.renderColumns(sel, ti, skipped); err != nil {
// return 0, err
// }
// io.WriteString(c.w, `)), '[]') AS "json"`)
// if sel.Paging.Type != qcode.PtOffset {
// for i, ob := range sel.OrderBy {
// io.WriteString(c.w, `, LAST_VALUE(`)
// colWithTableID(c.w, ti.Name, sel.ID, ob.Col)
// io.WriteString(c.w, `) OVER() AS "__cur_`)
// int2string(c.w, int32(i))
// io.WriteString(c.w, `"`)
// }
// io.WriteString(c.w, `LAST_VALUE(`)
// colWithTableID(c.w, ti.Name, sel.ID, ti.PrimaryCol.Name)
// io.WriteString(c.w, `) OVER() AS "__cursor_`)
// int2string(c.w, int32(len(sel.OrderBy)))
// io.WriteString(c.w, `"`)
//}
//}
io.WriteString(c.w, ` FROM (`) io.WriteString(c.w, ` FROM (`)
// FROM (SELECT .... ) // FROM (SELECT .... )
@ -988,8 +971,12 @@ func (c *compilerContext) renderOp(ex *qcode.Exp, ti *DBTableInfo) error {
switch ex.Op { switch ex.Op {
case qcode.OpEquals: case qcode.OpEquals:
io.WriteString(c.w, `IS NOT DISTINCT FROM`) io.WriteString(c.w, `=`)
case qcode.OpNotEquals: case qcode.OpNotEquals:
io.WriteString(c.w, `!=`)
case qcode.OpNotDistinct:
io.WriteString(c.w, `IS NOT DISTINCT FROM`)
case qcode.OpDistinct:
io.WriteString(c.w, `IS DISTINCT FROM`) io.WriteString(c.w, `IS DISTINCT FROM`)
case qcode.OpGreaterOrEquals: case qcode.OpGreaterOrEquals:
io.WriteString(c.w, `>=`) io.WriteString(c.w, `>=`)

View File

@ -39,61 +39,61 @@ WITH "_sg_input" AS (SELECT '{{upsert}}' :: json AS j), "products" AS (INSERT IN
=== RUN TestCompileMutate/bulkUpsert === RUN TestCompileMutate/bulkUpsert
WITH "_sg_input" AS (SELECT '{{upsert}}' :: json AS j), "products" AS (INSERT INTO "products" ("name", "description") SELECT "t"."name", "t"."description" FROM "_sg_input" i, json_populate_recordset(NULL::products, i.j) t RETURNING *) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, description = EXCLUDED.description RETURNING *) SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sel_0" WITH "_sg_input" AS (SELECT '{{upsert}}' :: json AS j), "products" AS (INSERT INTO "products" ("name", "description") SELECT "t"."name", "t"."description" FROM "_sg_input" i, json_populate_recordset(NULL::products, i.j) t RETURNING *) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, description = EXCLUDED.description RETURNING *) SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sel_0"
=== RUN TestCompileMutate/delete === RUN TestCompileMutate/delete
WITH "products" AS (DELETE FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))) AND (("products"."id") IS NOT DISTINCT FROM '1' :: bigint)) RETURNING "products".*) SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sel_0" WITH "products" AS (DELETE FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))) AND (("products"."id") = '1' :: bigint)) RETURNING "products".*) SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sel_0"
--- PASS: TestCompileMutate (0.00s) --- PASS: TestCompileMutate (0.01s)
--- PASS: TestCompileMutate/singleUpsert (0.00s) --- PASS: TestCompileMutate/singleUpsert (0.00s)
--- PASS: TestCompileMutate/singleUpsertWhere (0.00s) --- PASS: TestCompileMutate/singleUpsertWhere (0.00s)
--- PASS: TestCompileMutate/bulkUpsert (0.00s) --- PASS: TestCompileMutate/bulkUpsert (0.00s)
--- PASS: TestCompileMutate/delete (0.00s) --- PASS: TestCompileMutate/delete (0.00s)
=== RUN TestCompileQuery === RUN TestCompileQuery
=== RUN TestCompileQuery/withComplexArgs === RUN TestCompileQuery/withComplexArgs
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'price', "products_0"."price") AS "json" FROM (SELECT DISTINCT ON ("products"."price") "products"."id", "products"."name", "products"."price" FROM "products" WHERE (((("products"."id") < '28' :: bigint) AND (("products"."id") >= '20' :: bigint) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))))) ORDER BY "products"."price" DESC LIMIT ('30') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'price', "products_0"."price") AS "json" FROM (SELECT DISTINCT ON ("products"."price") "products"."id", "products"."name", "products"."price" FROM "products" WHERE (((("products"."id") < '28' :: bigint) AND (("products"."id") >= '20' :: bigint) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))))) ORDER BY "products"."price" DESC LIMIT ('30') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/withWhereAndList === RUN TestCompileQuery/withWhereAndList
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'price', "products_0"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE (((("products"."price") > '10' :: numeric(7,2)) AND NOT (("products"."id") IS NULL) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))))) LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'price', "products_0"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE (((("products"."price") > '10' :: numeric(7,2)) AND NOT (("products"."id") IS NULL) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))))) LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/withWhereIsNull === RUN TestCompileQuery/withWhereIsNull
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'price', "products_0"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE (((("products"."price") > '10' :: numeric(7,2)) AND NOT (("products"."id") IS NULL) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))))) LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'price', "products_0"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE (((("products"."price") > '10' :: numeric(7,2)) AND NOT (("products"."id") IS NULL) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))))) LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/withWhereMultiOr === RUN TestCompileQuery/withWhereMultiOr
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'price', "products_0"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))) AND ((("products"."price") < '20' :: numeric(7,2)) OR (("products"."price") > '10' :: numeric(7,2)) OR NOT (("products"."id") IS NULL)))) LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'price', "products_0"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))) AND ((("products"."price") < '20' :: numeric(7,2)) OR (("products"."price") > '10' :: numeric(7,2)) OR NOT (("products"."id") IS NULL)))) LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/fetchByID === RUN TestCompileQuery/fetchByID
SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE ((((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))) AND (("products"."id") = '{{id}}' :: bigint))) LIMIT ('1') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE ((((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))) AND (("products"."id") = '{{id}}' :: bigint))) LIMIT ('1') :: integer) AS "products_0") AS "__sel_0"
=== RUN TestCompileQuery/searchQuery === RUN TestCompileQuery/searchQuery
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'search_rank', "products_0"."search_rank", 'search_headline_description', "products_0"."search_headline_description") AS "json" FROM (SELECT "products"."id", "products"."name", ts_rank("products"."tsv", websearch_to_tsquery('{{query}}')) AS "search_rank", ts_headline("products"."description", websearch_to_tsquery('{{query}}')) AS "search_headline_description" FROM "products" WHERE ((("products"."tsv") @@ websearch_to_tsquery('{{query}}'))) LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'search_rank', "products_0"."search_rank", 'search_headline_description', "products_0"."search_headline_description") AS "json" FROM (SELECT "products"."id", "products"."name", ts_rank("products"."tsv", websearch_to_tsquery('{{query}}')) AS "search_rank", ts_headline("products"."description", websearch_to_tsquery('{{query}}')) AS "search_headline_description" FROM "products" WHERE ((("products"."tsv") @@ websearch_to_tsquery('{{query}}'))) LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/oneToMany === RUN TestCompileQuery/oneToMany
SELECT json_build_object('users', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('email', "users_0"."email", 'products', "__sel_1"."json") AS "json" FROM (SELECT "users"."email", "users"."id" FROM "users" LIMIT ('20') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_1"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_1"."name", 'price', "products_1"."price") AS "json" FROM (SELECT "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id")) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_1") AS "__sel_1") AS "__sel_1" ON ('true')) AS "__sel_0" SELECT json_build_object('users', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "users_0"."email", 'products', "__sel_1"."json") AS "json" FROM (SELECT "users"."email", "users"."id" FROM "users" LIMIT ('20') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_1"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_1"."name", 'price', "products_1"."price") AS "json" FROM (SELECT "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id")) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_1") AS "__sel_1") AS "__sel_1" ON ('true')) AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/oneToManyReverse === RUN TestCompileQuery/oneToManyReverse
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('name', "products_0"."name", 'price', "products_0"."price", 'users', "__sel_1"."json") AS "json" FROM (SELECT "products"."name", "products"."price", "products"."user_id" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_1"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "users_1"."email") AS "json" FROM (SELECT "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('20') :: integer) AS "users_1") AS "__sel_1") AS "__sel_1" ON ('true')) AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_0"."name", 'price', "products_0"."price", 'users', "__sel_1"."json") AS "json" FROM (SELECT "products"."name", "products"."price", "products"."user_id" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_1"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "users_1"."email") AS "json" FROM (SELECT "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('20') :: integer) AS "users_1") AS "__sel_1") AS "__sel_1" ON ('true')) AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/oneToManyArray === RUN TestCompileQuery/oneToManyArray
SELECT json_build_object('tags', coalesce(json_agg("__sel_0"."json"), '[]'), 'product', "__sel_2"."json") as "__root" FROM (SELECT json_build_object('name', "products_2"."name", 'price', "products_2"."price", 'tags', "__sel_3"."json") AS "json" FROM (SELECT "products"."name", "products"."price", "products"."tags" FROM "products" LIMIT ('1') :: integer) AS "products_2" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_3"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "tags_3"."id", 'name', "tags_3"."name") AS "json" FROM (SELECT "tags"."id", "tags"."name" FROM "tags" WHERE ((("tags"."slug") = any ("products_2"."tags"))) LIMIT ('20') :: integer) AS "tags_3") AS "__sel_3") AS "__sel_3" ON ('true')) AS "__sel_2", (SELECT json_build_object('name', "tags_0"."name", 'product', "__sel_1"."json") AS "json" FROM (SELECT "tags"."name", "tags"."slug" FROM "tags" LIMIT ('20') :: integer) AS "tags_0" LEFT OUTER JOIN LATERAL (SELECT json_build_object('name', "products_1"."name") AS "json" FROM (SELECT "products"."name" FROM "products" WHERE ((("tags_0"."slug") = any ("products"."tags"))) LIMIT ('1') :: integer) AS "products_1") AS "__sel_1" ON ('true')) AS "__sel_0" SELECT json_build_object('tags', "__sel_0"."json", 'product', "__sel_2"."json") as "__root" FROM (SELECT json_build_object('name', "products_2"."name", 'price', "products_2"."price", 'tags', "__sel_3"."json") AS "json" FROM (SELECT "products"."name", "products"."price", "products"."tags" FROM "products" LIMIT ('1') :: integer) AS "products_2" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_3"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "tags_3"."id", 'name', "tags_3"."name") AS "json" FROM (SELECT "tags"."id", "tags"."name" FROM "tags" WHERE ((("tags"."slug") = any ("products_2"."tags"))) LIMIT ('20') :: integer) AS "tags_3") AS "__sel_3") AS "__sel_3" ON ('true')) AS "__sel_2", (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "tags_0"."name", 'product', "__sel_1"."json") AS "json" FROM (SELECT "tags"."name", "tags"."slug" FROM "tags" LIMIT ('20') :: integer) AS "tags_0" LEFT OUTER JOIN LATERAL (SELECT json_build_object('name', "products_1"."name") AS "json" FROM (SELECT "products"."name" FROM "products" WHERE ((("tags_0"."slug") = any ("products"."tags"))) LIMIT ('1') :: integer) AS "products_1") AS "__sel_1" ON ('true')) AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/manyToMany === RUN TestCompileQuery/manyToMany
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('name', "products_0"."name", 'customers', "__sel_1"."json") AS "json" FROM (SELECT "products"."name", "products"."id" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_1"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "customers_1"."email", 'full_name', "customers_1"."full_name") AS "json" FROM (SELECT "customers"."email", "customers"."full_name" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_0"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('20') :: integer) AS "customers_1") AS "__sel_1") AS "__sel_1" ON ('true')) AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_0"."name", 'customers', "__sel_1"."json") AS "json" FROM (SELECT "products"."name", "products"."id" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_1"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "customers_1"."email", 'full_name', "customers_1"."full_name") AS "json" FROM (SELECT "customers"."email", "customers"."full_name" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_0"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('20') :: integer) AS "customers_1") AS "__sel_1") AS "__sel_1" ON ('true')) AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/manyToManyReverse === RUN TestCompileQuery/manyToManyReverse
SELECT json_build_object('customers', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('email', "customers_0"."email", 'full_name', "customers_0"."full_name", 'products', "__sel_1"."json") AS "json" FROM (SELECT "customers"."email", "customers"."full_name", "customers"."id" FROM "customers" LIMIT ('20') :: integer) AS "customers_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_1"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_1"."name") AS "json" FROM (SELECT "products"."name" FROM "products" LEFT OUTER JOIN "purchases" ON (("purchases"."customer_id") = ("customers_0"."id")) WHERE ((("products"."id") = ("purchases"."product_id")) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_1") AS "__sel_1") AS "__sel_1" ON ('true')) AS "__sel_0" SELECT json_build_object('customers', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "customers_0"."email", 'full_name', "customers_0"."full_name", 'products', "__sel_1"."json") AS "json" FROM (SELECT "customers"."email", "customers"."full_name", "customers"."id" FROM "customers" LIMIT ('20') :: integer) AS "customers_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_1"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_1"."name") AS "json" FROM (SELECT "products"."name" FROM "products" LEFT OUTER JOIN "purchases" ON (("purchases"."customer_id") = ("customers_0"."id")) WHERE ((("products"."id") = ("purchases"."product_id")) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('20') :: integer) AS "products_1") AS "__sel_1") AS "__sel_1" ON ('true')) AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/aggFunction === RUN TestCompileQuery/aggFunction
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('name', "products_0"."name", 'count_price', "products_0"."count_price") AS "json" FROM (SELECT "products"."name", price("products"."price") AS "count_price" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) GROUP BY "products"."name" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_0"."name", 'count_price', "products_0"."count_price") AS "json" FROM (SELECT "products"."name", price("products"."price") AS "count_price" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) GROUP BY "products"."name" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/aggFunctionBlockedByCol === RUN TestCompileQuery/aggFunctionBlockedByCol
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('name', "products_0"."name") AS "json" FROM (SELECT "products"."name" FROM "products" GROUP BY "products"."name" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_0"."name") AS "json" FROM (SELECT "products"."name" FROM "products" GROUP BY "products"."name" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/aggFunctionDisabled === RUN TestCompileQuery/aggFunctionDisabled
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('name', "products_0"."name") AS "json" FROM (SELECT "products"."name" FROM "products" GROUP BY "products"."name" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "products_0"."name") AS "json" FROM (SELECT "products"."name" FROM "products" GROUP BY "products"."name" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/aggFunctionWithFilter === RUN TestCompileQuery/aggFunctionWithFilter
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'max_price', "products_0"."max_price") AS "json" FROM (SELECT "products"."id", pri("products"."price") AS "max_price" FROM "products" WHERE ((((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))) AND (("products"."id") > '10' :: bigint))) GROUP BY "products"."id" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "products_0"."id", 'max_price', "products_0"."max_price") AS "json" FROM (SELECT "products"."id", pri("products"."price") AS "max_price" FROM "products" WHERE ((((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))) AND (("products"."id") > '10' :: bigint))) GROUP BY "products"."id" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/syntheticTables === RUN TestCompileQuery/syntheticTables
SELECT json_build_object('me', "__sel_0"."json") as "__root" FROM (SELECT json_build_object() AS "json" FROM (SELECT "users"."email" FROM "users" WHERE ((("users"."id") IS NOT DISTINCT FROM '{{user_id}}' :: bigint)) LIMIT ('1') :: integer) AS "users_0") AS "__sel_0" SELECT json_build_object('me', "__sel_0"."json") as "__root" FROM (SELECT json_build_object() AS "json" FROM (SELECT "users"."email" FROM "users" WHERE ((("users"."id") = '{{user_id}}' :: bigint)) LIMIT ('1') :: integer) AS "users_0") AS "__sel_0"
=== RUN TestCompileQuery/queryWithVariables === RUN TestCompileQuery/queryWithVariables
SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE (((("products"."price") IS NOT DISTINCT FROM '{{product_price}}' :: numeric(7,2)) AND (("products"."id") = '{{product_id}}' :: bigint) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))))) LIMIT ('1') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE (((("products"."price") = '{{product_price}}' :: numeric(7,2)) AND (("products"."id") = '{{product_id}}' :: bigint) AND ((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2))))) LIMIT ('1') :: integer) AS "products_0") AS "__sel_0"
=== RUN TestCompileQuery/withWhereOnRelations === RUN TestCompileQuery/withWhereOnRelations
SELECT json_build_object('users', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "users_0"."id", 'email', "users_0"."email") AS "json" FROM (SELECT "users"."id", "users"."email" FROM "users" WHERE (NOT EXISTS (SELECT 1 FROM products WHERE (("products"."user_id") = ("users"."id")) AND ((("products"."price") > '3' :: numeric(7,2))))) LIMIT ('20') :: integer) AS "users_0") AS "__sel_0" SELECT json_build_object('users', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "users_0"."id", 'email', "users_0"."email") AS "json" FROM (SELECT "users"."id", "users"."email" FROM "users" WHERE (NOT EXISTS (SELECT 1 FROM products WHERE (("products"."user_id") = ("users"."id")) AND ((("products"."price") > '3' :: numeric(7,2))))) LIMIT ('20') :: integer) AS "users_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/multiRoot === RUN TestCompileQuery/multiRoot
SELECT json_build_object('customer', "__sel_0"."json", 'user', "__sel_1"."json", 'product', "__sel_2"."json") as "__root" FROM (SELECT json_build_object('id', "products_2"."id", 'name', "products_2"."name", 'customers', "__sel_3"."json", 'customer', "__sel_4"."json") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('1') :: integer) AS "products_2" LEFT OUTER JOIN LATERAL (SELECT json_build_object('email', "customers_4"."email") AS "json" FROM (SELECT "customers"."email" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_2"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('1') :: integer) AS "customers_4") AS "__sel_4" ON ('true') LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_3"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "customers_3"."email") AS "json" FROM (SELECT "customers"."email" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_2"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('20') :: integer) AS "customers_3") AS "__sel_3") AS "__sel_3" ON ('true')) AS "__sel_2", (SELECT json_build_object('id', "users_1"."id", 'email', "users_1"."email") AS "json" FROM (SELECT "users"."id", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_1") AS "__sel_1", (SELECT json_build_object('id', "customers_0"."id") AS "json" FROM (SELECT "customers"."id" FROM "customers" LIMIT ('1') :: integer) AS "customers_0") AS "__sel_0" SELECT json_build_object('customer', "__sel_0"."json", 'user', "__sel_1"."json", 'product', "__sel_2"."json") as "__root" FROM (SELECT json_build_object('id', "products_2"."id", 'name', "products_2"."name", 'customers', "__sel_3"."json", 'customer', "__sel_4"."json") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" WHERE (((("products"."price") > '0' :: numeric(7,2)) AND (("products"."price") < '8' :: numeric(7,2)))) LIMIT ('1') :: integer) AS "products_2" LEFT OUTER JOIN LATERAL (SELECT json_build_object('email', "customers_4"."email") AS "json" FROM (SELECT "customers"."email" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_2"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('1') :: integer) AS "customers_4") AS "__sel_4" ON ('true') LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_3"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "customers_3"."email") AS "json" FROM (SELECT "customers"."email" FROM "customers" LEFT OUTER JOIN "purchases" ON (("purchases"."product_id") = ("products_2"."id")) WHERE ((("customers"."id") = ("purchases"."customer_id"))) LIMIT ('20') :: integer) AS "customers_3") AS "__sel_3") AS "__sel_3" ON ('true')) AS "__sel_2", (SELECT json_build_object('id', "users_1"."id", 'email', "users_1"."email") AS "json" FROM (SELECT "users"."id", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_1") AS "__sel_1", (SELECT json_build_object('id', "customers_0"."id") AS "json" FROM (SELECT "customers"."id" FROM "customers" LIMIT ('1') :: integer) AS "customers_0") AS "__sel_0"
=== RUN TestCompileQuery/jsonColumnAsTable === RUN TestCompileQuery/jsonColumnAsTable
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'tag_count', "__sel_1"."json") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT json_build_object('count', "tag_count_1"."count", 'tags', "__sel_2"."json") AS "json" FROM (SELECT "tag_count"."count", "tag_count"."tag_id" FROM "products", json_to_recordset("products"."tag_count") AS "tag_count"(tag_id bigint, count int) WHERE ((("products"."id") = ("products_0"."id"))) LIMIT ('1') :: integer) AS "tag_count_1" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_2"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "tags_2"."name") AS "json" FROM (SELECT "tags"."name" FROM "tags" WHERE ((("tags"."id") = ("tag_count_1"."tag_id"))) LIMIT ('20') :: integer) AS "tags_2") AS "__sel_2") AS "__sel_2" ON ('true')) AS "__sel_1" ON ('true')) AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'tag_count', "__sel_1"."json") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('20') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT json_build_object('count', "tag_count_1"."count", 'tags', "__sel_2"."json") AS "json" FROM (SELECT "tag_count"."count", "tag_count"."tag_id" FROM "products", json_to_recordset("products"."tag_count") AS "tag_count"(tag_id bigint, count int) WHERE ((("products"."id") = ("products_0"."id"))) LIMIT ('1') :: integer) AS "tag_count_1" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("__sel_2"."json"), '[]') as "json" FROM (SELECT json_build_object('name', "tags_2"."name") AS "json" FROM (SELECT "tags"."name" FROM "tags" WHERE ((("tags"."id") = ("tag_count_1"."tag_id"))) LIMIT ('20') :: integer) AS "tags_2") AS "__sel_2") AS "__sel_2" ON ('true')) AS "__sel_1" ON ('true')) AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/withCursor === RUN TestCompileQuery/withCursor
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]'), 'products_cursor', CONCAT_WS(',', max("__cur_0"), max("__cur_1"))) as "__root" FROM (SELECT json_build_object('name', "products_0"."name") AS "json", LAST_VALUE("products_0"."price") OVER() AS "__cur_0", LAST_VALUE("products_0"."id") OVER() AS "__cur_1" FROM (WITH "__cur" AS (SELECT a[1] as "price", a[2] as "id" FROM string_to_array('{{cursor}}', ',') as a) SELECT "products"."name", "products"."id", "products"."price" FROM "products", "__cur" WHERE (((("products"."id") > "__cur"."id" :: bigint) AND (("products"."price") < "__cur"."price" :: numeric(7,2)))) ORDER BY "products"."price" DESC, "products"."id" ASC LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json", 'products_cursor', "__sel_0"."cursor") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json", CONCAT_WS(',', max("__cur_0"), max("__cur_1")) as "cursor" FROM (SELECT json_build_object('name', "products_0"."name") AS "json", LAST_VALUE("products_0"."price") OVER() AS "__cur_0", LAST_VALUE("products_0"."id") OVER() AS "__cur_1" FROM (WITH "__cur" AS (SELECT a[1] as "price", a[2] as "id" FROM string_to_array('{{cursor}}', ',') as a) SELECT "products"."name", "products"."id", "products"."price" FROM "products", "__cur" WHERE (((("products"."price") < "__cur"."price" :: numeric(7,2)) OR ((("products"."price") = "__cur"."price" :: numeric(7,2)) AND (("products"."id") > "__cur"."id" :: bigint)))) ORDER BY "products"."price" DESC, "products"."id" ASC LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/skipUserIDForAnonRole === RUN TestCompileQuery/skipUserIDForAnonRole
SELECT json_build_object('products', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0" SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('20') :: integer) AS "products_0") AS "__sel_0") AS "__sel_0"
=== RUN TestCompileQuery/blockedQuery === RUN TestCompileQuery/blockedQuery
SELECT json_build_object('user', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "users_0"."id", 'full_name', "users_0"."full_name", 'email', "users_0"."email") AS "json" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE (false) LIMIT ('1') :: integer) AS "users_0") AS "__sel_0" SELECT json_build_object('user', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "users_0"."id", 'full_name', "users_0"."full_name", 'email', "users_0"."email") AS "json" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE (false) LIMIT ('1') :: integer) AS "users_0") AS "__sel_0"
=== RUN TestCompileQuery/blockedFunctions === RUN TestCompileQuery/blockedFunctions
SELECT json_build_object('users', coalesce(json_agg("__sel_0"."json"), '[]')) as "__root" FROM (SELECT json_build_object('email', "users_0"."email") AS "json" FROM (SELECT , "users"."email" FROM "users" WHERE (false) GROUP BY "users"."email" LIMIT ('20') :: integer) AS "users_0") AS "__sel_0" SELECT json_build_object('users', "__sel_0"."json") as "__root" FROM (SELECT coalesce(json_agg("__sel_0"."json"), '[]') as "json" FROM (SELECT json_build_object('email', "users_0"."email") AS "json" FROM (SELECT , "users"."email" FROM "users" WHERE (false) GROUP BY "users"."email" LIMIT ('20') :: integer) AS "users_0") AS "__sel_0") AS "__sel_0"
--- PASS: TestCompileQuery (0.02s) --- PASS: TestCompileQuery (0.02s)
--- PASS: TestCompileQuery/withComplexArgs (0.00s) --- PASS: TestCompileQuery/withComplexArgs (0.00s)
--- PASS: TestCompileQuery/withWhereAndList (0.00s) --- PASS: TestCompileQuery/withWhereAndList (0.00s)
@ -121,14 +121,14 @@ SELECT json_build_object('users', coalesce(json_agg("__sel_0"."json"), '[]')) as
--- PASS: TestCompileQuery/blockedFunctions (0.00s) --- PASS: TestCompileQuery/blockedFunctions (0.00s)
=== RUN TestCompileUpdate === RUN TestCompileUpdate
=== RUN TestCompileUpdate/singleUpdate === RUN TestCompileUpdate/singleUpdate
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") IS NOT DISTINCT FROM '1' :: bigint) AND (("products"."id") = '{{id}}' :: bigint)) RETURNING "products".*) SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sel_0" 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' :: bigint) AND (("products"."id") = '{{id}}' :: bigint)) RETURNING "products".*) SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name") AS "json" FROM (SELECT "products"."id", "products"."name" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sel_0"
=== RUN TestCompileUpdate/simpleUpdateWithPresets === RUN TestCompileUpdate/simpleUpdateWithPresets
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") IS NOT DISTINCT FROM '{{user_id}}' :: bigint) RETURNING "products".*) SELECT json_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id") AS "json" FROM (SELECT "products"."id" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sel_0" 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_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id") AS "json" FROM (SELECT "products"."id" FROM "products" LIMIT ('1') :: integer) AS "products_0") AS "__sel_0"
=== RUN TestCompileUpdate/nestedUpdateManyToMany === RUN TestCompileUpdate/nestedUpdateManyToMany
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") = '{{id}}' :: bigint) 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_build_object('purchase', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('sale_type', "purchases_0"."sale_type", 'quantity', "purchases_0"."quantity", 'due_date', "purchases_0"."due_date", 'product', "__sel_1"."json", 'customer', "__sel_2"."json") AS "json" 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 json_build_object('id', "customers_2"."id", 'full_name', "customers_2"."full_name", 'email', "customers_2"."email") AS "json" FROM (SELECT "customers"."id", "customers"."full_name", "customers"."email" FROM "customers" WHERE ((("customers"."id") = ("purchases_0"."customer_id"))) LIMIT ('1') :: integer) AS "customers_2") AS "__sel_2" ON ('true') LEFT OUTER JOIN LATERAL (SELECT json_build_object('id', "products_1"."id", 'name', "products_1"."name", 'price', "products_1"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."id") = ("purchases_0"."product_id"))) LIMIT ('1') :: integer) AS "products_1") AS "__sel_1" ON ('true')) AS "__sel_0"
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") = '{{id}}' :: bigint) 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_build_object('purchase', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('sale_type', "purchases_0"."sale_type", 'quantity', "purchases_0"."quantity", 'due_date', "purchases_0"."due_date", 'product', "__sel_1"."json", 'customer', "__sel_2"."json") AS "json" 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 json_build_object('id', "customers_2"."id", 'full_name', "customers_2"."full_name", 'email', "customers_2"."email") AS "json" FROM (SELECT "customers"."id", "customers"."full_name", "customers"."email" FROM "customers" WHERE ((("customers"."id") = ("purchases_0"."customer_id"))) LIMIT ('1') :: integer) AS "customers_2") AS "__sel_2" ON ('true') LEFT OUTER JOIN LATERAL (SELECT json_build_object('id', "products_1"."id", 'name', "products_1"."name", 'price', "products_1"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."id") = ("purchases_0"."product_id"))) LIMIT ('1') :: integer) AS "products_1") AS "__sel_1" ON ('true')) AS "__sel_0" 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") = '{{id}}' :: bigint) 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_build_object('purchase', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('sale_type', "purchases_0"."sale_type", 'quantity', "purchases_0"."quantity", 'due_date', "purchases_0"."due_date", 'product', "__sel_1"."json", 'customer', "__sel_2"."json") AS "json" 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 json_build_object('id', "customers_2"."id", 'full_name', "customers_2"."full_name", 'email', "customers_2"."email") AS "json" FROM (SELECT "customers"."id", "customers"."full_name", "customers"."email" FROM "customers" WHERE ((("customers"."id") = ("purchases_0"."customer_id"))) LIMIT ('1') :: integer) AS "customers_2") AS "__sel_2" ON ('true') LEFT OUTER JOIN LATERAL (SELECT json_build_object('id', "products_1"."id", 'name', "products_1"."name", 'price', "products_1"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."id") = ("purchases_0"."product_id"))) LIMIT ('1') :: integer) AS "products_1") AS "__sel_1" ON ('true')) AS "__sel_0"
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") = '{{id}}' :: bigint) 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_build_object('purchase', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('sale_type', "purchases_0"."sale_type", 'quantity', "purchases_0"."quantity", 'due_date', "purchases_0"."due_date", 'product', "__sel_1"."json", 'customer', "__sel_2"."json") AS "json" 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 json_build_object('id', "customers_2"."id", 'full_name', "customers_2"."full_name", 'email', "customers_2"."email") AS "json" FROM (SELECT "customers"."id", "customers"."full_name", "customers"."email" FROM "customers" WHERE ((("customers"."id") = ("purchases_0"."customer_id"))) LIMIT ('1') :: integer) AS "customers_2") AS "__sel_2" ON ('true') LEFT OUTER JOIN LATERAL (SELECT json_build_object('id', "products_1"."id", 'name', "products_1"."name", 'price', "products_1"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."id") = ("purchases_0"."product_id"))) LIMIT ('1') :: integer) AS "products_1") AS "__sel_1" ON ('true')) AS "__sel_0"
=== RUN TestCompileUpdate/nestedUpdateOneToMany === RUN TestCompileUpdate/nestedUpdateOneToMany
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") IS NOT DISTINCT FROM '8' :: bigint) 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"= ((i.j->'product'->'where'->>'id'))::bigint) RETURNING "products".*) SELECT json_build_object('user', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "users_0"."id", 'full_name', "users_0"."full_name", 'email', "users_0"."email", 'product', "__sel_1"."json") AS "json" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT json_build_object('id', "products_1"."id", 'name', "products_1"."name", 'price', "products_1"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1") AS "__sel_1" ON ('true')) AS "__sel_0" 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' :: bigint) 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"= ((i.j->'product'->'where'->>'id'))::bigint) RETURNING "products".*) SELECT json_build_object('user', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "users_0"."id", 'full_name', "users_0"."full_name", 'email', "users_0"."email", 'product', "__sel_1"."json") AS "json" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT json_build_object('id', "products_1"."id", 'name', "products_1"."name", 'price', "products_1"."price") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1") AS "__sel_1" ON ('true')) AS "__sel_0"
=== RUN TestCompileUpdate/nestedUpdateOneToOne === RUN TestCompileUpdate/nestedUpdateOneToOne
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") = '{{id}}' :: bigint) 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_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'user', "__sel_1"."json") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT json_build_object('id', "users_1"."id", 'full_name', "users_1"."full_name", 'email', "users_1"."email") AS "json" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1") AS "__sel_1" ON ('true')) AS "__sel_0" 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") = '{{id}}' :: bigint) 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_build_object('product', "__sel_0"."json") as "__root" FROM (SELECT json_build_object('id', "products_0"."id", 'name', "products_0"."name", 'user', "__sel_1"."json") AS "json" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT json_build_object('id', "users_1"."id", 'full_name', "users_1"."full_name", 'email', "users_1"."email") AS "json" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1") AS "__sel_1" ON ('true')) AS "__sel_0"
=== RUN TestCompileUpdate/nestedUpdateOneToManyWithConnect === RUN TestCompileUpdate/nestedUpdateOneToManyWithConnect
@ -148,4 +148,4 @@ WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "_x_users" AS (SELECT * FR
--- PASS: TestCompileUpdate/nestedUpdateOneToOneWithConnect (0.00s) --- PASS: TestCompileUpdate/nestedUpdateOneToOneWithConnect (0.00s)
--- PASS: TestCompileUpdate/nestedUpdateOneToOneWithDisconnect (0.00s) --- PASS: TestCompileUpdate/nestedUpdateOneToOneWithDisconnect (0.00s)
PASS PASS
ok github.com/dosco/super-graph/psql 0.130s ok github.com/dosco/super-graph/psql 0.716s

View File

@ -131,6 +131,8 @@ const (
OpEqID OpEqID
OpTsQuery OpTsQuery
OpFalse OpFalse
OpNotDistinct
OpDistinct
) )
type ValType int type ValType int
@ -195,10 +197,9 @@ func NewCompiler(c Config) (*Compiler, error) {
return co, nil return co, nil
} }
func AddFilter(sel *Select) *Exp { func NewFilter() *Exp {
ex := expPool.Get().(*Exp) ex := expPool.Get().(*Exp)
ex.Reset() ex.Reset()
addFilter(sel, ex)
return ex return ex
} }
@ -363,8 +364,8 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
return err return err
} }
// Order is important addFilters must come after compileArgs // Order is important AddFilters must come after compileArgs
com.addFilters(qc, s, role) com.AddFilters(qc, s, role)
if s.ParentID == -1 { if s.ParentID == -1 {
qc.Roots = append(qc.Roots, s.ID) qc.Roots = append(qc.Roots, s.ID)
@ -410,7 +411,7 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
return nil return nil
} }
func (com *Compiler) addFilters(qc *QCode, sel *Select, role string) { func (com *Compiler) AddFilters(qc *QCode, sel *Select, role string) {
var fil *Exp var fil *Exp
var nu bool var nu bool
@ -435,7 +436,7 @@ func (com *Compiler) addFilters(qc *QCode, sel *Select, role string) {
case OpFalse: case OpFalse:
sel.Where = fil sel.Where = fil
default: default:
addFilter(sel, fil) AddFilter(sel, fil)
} }
} }
@ -659,7 +660,7 @@ func (com *Compiler) compileArgSearch(sel *Select, arg *Arg) (error, bool) {
} }
sel.Args[arg.Name] = arg.Val sel.Args[arg.Name] = arg.Val
addFilter(sel, ex) AddFilter(sel, ex)
return nil, true return nil, true
} }
@ -676,7 +677,7 @@ func (com *Compiler) compileArgWhere(sel *Select, arg *Arg, role string) (error,
if nu && role == "anon" { if nu && role == "anon" {
sel.SkipRender = true sel.SkipRender = true
} }
addFilter(sel, ex) AddFilter(sel, ex)
return nil, true return nil, true
} }
@ -820,11 +821,11 @@ func (com *Compiler) getRole(role, field string) *trval {
} }
} }
func addFilter(sel *Select, fil *Exp) { func AddFilter(sel *Select, fil *Exp) {
if sel.Where != nil { if sel.Where != nil {
ow := sel.Where ow := sel.Where
if sel.Where.Op != OpAnd { if sel.Where.Op != OpAnd || !sel.Where.doFree {
sel.Where = expPool.Get().(*Exp) sel.Where = expPool.Get().(*Exp)
sel.Where.Reset() sel.Where.Reset()
sel.Where.Op = OpAnd sel.Where.Op = OpAnd
@ -937,6 +938,12 @@ func newExp(st *util.Stack, node *Node, usePool bool) (*Exp, error) {
case "is_null": case "is_null":
ex.Op = OpIsNull ex.Op = OpIsNull
ex.Val = node.Val ex.Val = node.Val
case "null_eq", "ndis", "not_distinct":
ex.Op = OpNotDistinct
ex.Val = node.Val
case "null_neq", "dis", "distinct":
ex.Op = OpDistinct
ex.Val = node.Val
default: default:
pushChildren(st, node.exp, node) pushChildren(st, node.exp, node)
return nil, nil // skip node return nil, nil // skip node

View File

@ -39,11 +39,16 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
} }
v := fields[0].Value v := fields[0].Value
// Open and close quotes
if len(v) >= 2 && v[0] == '"' && v[len(v)-1] == '"' { if len(v) >= 2 && v[0] == '"' && v[len(v)-1] == '"' {
fields[0].Value = v[1 : len(v)-1] fields[0].Value = v[1 : len(v)-1]
} }
if tag == "cursor" { if tag == "cursor" {
if bytes.EqualFold(v, []byte("null")) {
return io.WriteString(w, ``)
}
v1, err := decrypt(string(fields[0].Value)) v1, err := decrypt(string(fields[0].Value))
if err != nil { if err != nil {
return 0, err return 0, err
@ -52,6 +57,8 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
return w.Write(v1) return w.Write(v1)
} }
fmt.Println(">>>", tag, string(v))
return w.Write(escQuote(fields[0].Value)) return w.Write(escQuote(fields[0].Value))
} }
} }

View File

@ -19,11 +19,13 @@ func cmdServ(cmd *cobra.Command, args []string) {
fatalInProd(err, "failed to connect to database") fatalInProd(err, "failed to connect to database")
} }
initCrypto() if conf != nil && db != nil {
initCompiler() initCrypto()
initResolvers() initCompiler()
initAllowList(confPath) initResolvers()
initPreparedList(confPath) initAllowList(confPath)
initPreparedList(confPath)
}
startHTTP() startHTTP()
} }

View File

@ -36,15 +36,20 @@ func encryptCursor(qc *qcode.QCode, data []byte) ([]byte, error) {
continue continue
} }
v, err := crypto.Encrypt(f.Value[1:len(f.Value)-1], &internalKey)
if err != nil {
return nil, err
}
var buf bytes.Buffer var buf bytes.Buffer
buf.WriteByte('"')
buf.WriteString(base64.StdEncoding.EncodeToString(v)) if len(f.Value) > 2 {
buf.WriteByte('"') v, err := crypto.Encrypt(f.Value[1:len(f.Value)-1], &internalKey)
if err != nil {
return nil, err
}
buf.WriteByte('"')
buf.WriteString(base64.StdEncoding.EncodeToString(v))
buf.WriteByte('"')
} else {
buf.WriteString(`null`)
}
to[i].Value = buf.Bytes() to[i].Value = buf.Bytes()
} }

View File

@ -5,6 +5,7 @@ import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"os" "os"
"time"
"github.com/dosco/super-graph/allow" "github.com/dosco/super-graph/allow"
"github.com/dosco/super-graph/crypto" "github.com/dosco/super-graph/crypto"
@ -135,7 +136,17 @@ func initDBPool(c *config) (*pgxpool.Pool, error) {
config.MaxConns = conf.DB.PoolSize config.MaxConns = conf.DB.PoolSize
} }
db, err := pgxpool.ConnectConfig(context.Background(), config) var db *pgxpool.Pool
var err error
for i := 1; i < 10; i++ {
db, err = pgxpool.ConnectConfig(context.Background(), config)
if err == nil {
break
}
time.Sleep(time.Duration(i*100) * time.Millisecond)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }

File diff suppressed because one or more lines are too long

View File

@ -109,7 +109,7 @@ database:
port: 5432 port: 5432
dbname: {% app_name_slug %}_development dbname: {% app_name_slug %}_development
user: postgres user: postgres
password: '' password: postgres
#schema: "public" #schema: "public"
#pool_size: 10 #pool_size: 10

View File

@ -2,7 +2,10 @@ version: '3.4'
services: services:
# Postgres DB # Postgres DB
db: db:
image: postgres:latest image: postgres:12
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
ports: ports:
- "5432:5432" - "5432:5432"

View File

@ -54,7 +54,7 @@ database:
port: 5432 port: 5432
dbname: {% app_name_slug %}_development dbname: {% app_name_slug %}_development
user: postgres user: postgres
password: '' password: postgres
#pool_size: 10 #pool_size: 10
#max_retries: 0 #max_retries: 0
#log_level: "debug" #log_level: "debug"