Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
7acf28bb3c | |||
be5d4e976a | |||
d1b884aec6 | |||
4be4ce860b | |||
dfa4caf540 | |||
7763251fb7 | |||
51e105699e | |||
90694f8803 | |||
ad82f5b267 | |||
99b37a9c50 | |||
7ec1f59224 |
4
Makefile
4
Makefile
@ -35,7 +35,7 @@ $(GORICE):
|
||||
|
||||
$(WEB_BUILD_DIR):
|
||||
@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
|
||||
|
||||
$(GITCHGLOG):
|
||||
@ -77,7 +77,7 @@ clean:
|
||||
run: clean
|
||||
@go run $(BUILD_FLAGS) main.go $(ARGS)
|
||||
|
||||
install:
|
||||
install: gen
|
||||
@echo $(GOPATH)
|
||||
@echo "Commit Hash: `git rev-parse HEAD`"
|
||||
@echo "Old Hash: `shasum $(GOPATH)/bin/$(BINARY) 2>/dev/null | cut -c -32`"
|
||||
|
@ -93,7 +93,7 @@ database:
|
||||
port: 5432
|
||||
dbname: app_development
|
||||
user: postgres
|
||||
password: ''
|
||||
password: postgres
|
||||
|
||||
#schema: "public"
|
||||
#pool_size: 10
|
||||
|
@ -54,7 +54,7 @@ database:
|
||||
port: 5432
|
||||
dbname: app_production
|
||||
user: postgres
|
||||
password: ''
|
||||
password: postgres
|
||||
#pool_size: 10
|
||||
#max_retries: 0
|
||||
#log_level: "debug"
|
||||
|
@ -1,7 +1,10 @@
|
||||
version: '3.4'
|
||||
services:
|
||||
db:
|
||||
image: postgres
|
||||
image: postgres:12
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
|
@ -34,6 +34,12 @@ Super Graph has a rich feature set like integrating with your existing Ruby on R
|
||||
# clone the repository
|
||||
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
|
||||
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
|
||||
query {
|
||||
products(limit 5, where: { price: { gt: 12 } }) {
|
||||
products(limit: 5, where: { price: { gt: 12 } }) {
|
||||
id
|
||||
name
|
||||
description
|
||||
@ -153,7 +159,7 @@ query {
|
||||
}
|
||||
}
|
||||
purchases(
|
||||
limit 10,
|
||||
limit: 10,
|
||||
order_by: { created_at: desc } ,
|
||||
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
|
||||
```
|
||||
|
||||
And then create and launch you're new app
|
||||
And then create and launch your new app
|
||||
|
||||
```bash
|
||||
# create a new app and change to it's directory
|
||||
@ -1133,7 +1139,7 @@ query {
|
||||
|
||||
## 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.
|
||||
|
||||
```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.
|
||||
|
||||
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
|
||||
|
||||
@ -1767,7 +1773,7 @@ database:
|
||||
port: 5432
|
||||
dbname: app_development
|
||||
user: postgres
|
||||
password: ''
|
||||
password: postgres
|
||||
|
||||
#schema: "public"
|
||||
#pool_size: 10
|
||||
|
@ -13,7 +13,7 @@ Super Graph code is made up of a number of packages. We have done our best to ke
|
||||
|
||||
## 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`.
|
||||
|
||||
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
|
||||
type Operation struct {
|
||||
@ -238,4 +238,4 @@ ok github.com/dosco/super-graph/psql 2.530s
|
||||
|
||||
## 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.
|
||||
|
@ -19,7 +19,7 @@ default: &default
|
||||
encoding: unicode
|
||||
host: db
|
||||
username: postgres
|
||||
password:
|
||||
password: postgres
|
||||
pool: 5
|
||||
|
||||
development:
|
||||
|
@ -27,14 +27,20 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error {
|
||||
|
||||
var k []byte
|
||||
state := expectKey
|
||||
instr := false
|
||||
|
||||
for i := 0; i < len(b); i++ {
|
||||
if state == expectObjClose || state == expectListClose {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
if b[i-1] != '\\' && b[i] == '"' {
|
||||
instr = !instr
|
||||
}
|
||||
if !instr {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
17
jsn/get.go
17
jsn/get.go
@ -51,13 +51,20 @@ func Get(b []byte, keys [][]byte) []Field {
|
||||
state := expectKey
|
||||
|
||||
n := 0
|
||||
instr := false
|
||||
|
||||
for i := 0; i < len(b); i++ {
|
||||
if state == expectObjClose || state == expectListClose {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
if b[i-1] != '\\' && b[i] == '"' {
|
||||
instr = !instr
|
||||
}
|
||||
if !instr {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package jsn
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@ -161,6 +162,8 @@ var (
|
||||
|
||||
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}`
|
||||
|
||||
input7, _ = ioutil.ReadFile("test.json")
|
||||
)
|
||||
|
||||
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) {
|
||||
v1 := []byte("12345")
|
||||
if !bytes.Equal(Value(v1), v1) {
|
||||
|
17
jsn/keys.go
17
jsn/keys.go
@ -10,15 +10,20 @@ func Keys(b []byte) [][]byte {
|
||||
|
||||
st := NewStack()
|
||||
ae := 0
|
||||
instr := false
|
||||
|
||||
for i := 0; i < len(b); i++ {
|
||||
|
||||
if state == expectObjClose || state == expectListClose {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
if b[i-1] != '\\' && b[i] == '"' {
|
||||
instr = !instr
|
||||
}
|
||||
if !instr {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,8 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
|
||||
state := expectKey
|
||||
ws, we := -1, len(b)
|
||||
|
||||
instr := false
|
||||
|
||||
for i := 0; i < len(b); i++ {
|
||||
// skip any left padding whitespace
|
||||
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 {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
if b[i-1] != '\\' && b[i] == '"' {
|
||||
instr = !instr
|
||||
}
|
||||
if !instr {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
16
jsn/strip.go
16
jsn/strip.go
@ -11,14 +11,20 @@ func Strip(b []byte, path [][]byte) []byte {
|
||||
pi := 0
|
||||
pm := false
|
||||
state := expectKey
|
||||
instr := false
|
||||
|
||||
for i := 0; i < len(b); i++ {
|
||||
if state == expectObjClose || state == expectListClose {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
if b[i-1] != '\\' && b[i] == '"' {
|
||||
instr = !instr
|
||||
}
|
||||
if !instr {
|
||||
switch b[i] {
|
||||
case '{', '[':
|
||||
d++
|
||||
case '}', ']':
|
||||
d--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
1
jsn/test.json
Normal file
1
jsn/test.json
Normal file
File diff suppressed because one or more lines are too long
148
psql/query.go
148
psql/query.go
@ -267,58 +267,29 @@ func (c *compilerContext) initSelect(sel *qcode.Select, ti *DBTableInfo, vars Va
|
||||
|
||||
if sel.Paging.Type != qcode.PtOffset {
|
||||
colmap[ti.PrimaryCol.Key] = struct{}{}
|
||||
|
||||
addToOrderBy := true
|
||||
addPrimaryKey := true
|
||||
|
||||
for _, ob := range sel.OrderBy {
|
||||
if ob.Col == ti.PrimaryCol.Key {
|
||||
addToOrderBy = false
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
addPrimaryKey = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if addToOrderBy {
|
||||
var op qcode.ExpOp
|
||||
if addPrimaryKey {
|
||||
ob := &qcode.OrderBy{Col: ti.PrimaryCol.Name, Order: qcode.OrderAsc}
|
||||
|
||||
ob := &qcode.OrderBy{Col: ti.PrimaryCol.Name}
|
||||
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
|
||||
if sel.Paging.Type == qcode.PtBackward {
|
||||
ob.Order = qcode.OrderDesc
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
sel.OrderBy = append(sel.OrderBy, ob)
|
||||
}
|
||||
}
|
||||
|
||||
if sel.Paging.Cursor {
|
||||
c.addSeekPredicate(sel)
|
||||
}
|
||||
|
||||
for _, id := range sel.Children {
|
||||
child := &c.s[id]
|
||||
|
||||
@ -364,6 +335,72 @@ func (c *compilerContext) initSelect(sel *qcode.Select, ti *DBTableInfo, vars Va
|
||||
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) {
|
||||
var rel *DBRel
|
||||
var err error
|
||||
@ -399,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 (`)
|
||||
|
||||
// FROM (SELECT .... )
|
||||
@ -959,8 +971,12 @@ func (c *compilerContext) renderOp(ex *qcode.Exp, ti *DBTableInfo) error {
|
||||
|
||||
switch ex.Op {
|
||||
case qcode.OpEquals:
|
||||
io.WriteString(c.w, `IS NOT DISTINCT FROM`)
|
||||
io.WriteString(c.w, `=`)
|
||||
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`)
|
||||
case qcode.OpGreaterOrEquals:
|
||||
io.WriteString(c.w, `>=`)
|
||||
|
@ -39,7 +39,7 @@ WITH "_sg_input" AS (SELECT '{{upsert}}' :: json AS j), "products" AS (INSERT IN
|
||||
=== 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"
|
||||
=== 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.01s)
|
||||
--- PASS: TestCompileMutate/singleUpsert (0.00s)
|
||||
--- PASS: TestCompileMutate/singleUpsertWhere (0.00s)
|
||||
@ -77,9 +77,9 @@ SELECT json_build_object('products', "__sel_0"."json") as "__root" FROM (SELECT
|
||||
=== RUN TestCompileQuery/aggFunctionWithFilter
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
@ -87,7 +87,7 @@ SELECT json_build_object('customer', "__sel_0"."json", 'user', "__sel_1"."json",
|
||||
=== RUN TestCompileQuery/jsonColumnAsTable
|
||||
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
|
||||
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"."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") 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
|
||||
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
|
||||
@ -121,14 +121,14 @@ SELECT json_build_object('users', "__sel_0"."json") as "__root" FROM (SELECT coa
|
||||
--- PASS: TestCompileQuery/blockedFunctions (0.00s)
|
||||
=== RUN TestCompileUpdate
|
||||
=== 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
|
||||
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
|
||||
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".*), "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
|
||||
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
|
||||
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
|
||||
@ -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/nestedUpdateOneToOneWithDisconnect (0.00s)
|
||||
PASS
|
||||
ok github.com/dosco/super-graph/psql 0.456s
|
||||
ok github.com/dosco/super-graph/psql 0.716s
|
||||
|
@ -131,6 +131,8 @@ const (
|
||||
OpEqID
|
||||
OpTsQuery
|
||||
OpFalse
|
||||
OpNotDistinct
|
||||
OpDistinct
|
||||
)
|
||||
|
||||
type ValType int
|
||||
@ -195,10 +197,9 @@ func NewCompiler(c Config) (*Compiler, error) {
|
||||
return co, nil
|
||||
}
|
||||
|
||||
func AddFilter(sel *Select) *Exp {
|
||||
func NewFilter() *Exp {
|
||||
ex := expPool.Get().(*Exp)
|
||||
ex.Reset()
|
||||
addFilter(sel, ex)
|
||||
|
||||
return ex
|
||||
}
|
||||
@ -363,8 +364,8 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Order is important addFilters must come after compileArgs
|
||||
com.addFilters(qc, s, role)
|
||||
// Order is important AddFilters must come after compileArgs
|
||||
com.AddFilters(qc, s, role)
|
||||
|
||||
if s.ParentID == -1 {
|
||||
qc.Roots = append(qc.Roots, s.ID)
|
||||
@ -410,7 +411,7 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||
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 nu bool
|
||||
|
||||
@ -435,7 +436,7 @@ func (com *Compiler) addFilters(qc *QCode, sel *Select, role string) {
|
||||
case OpFalse:
|
||||
sel.Where = fil
|
||||
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
|
||||
addFilter(sel, ex)
|
||||
AddFilter(sel, ex)
|
||||
|
||||
return nil, true
|
||||
}
|
||||
@ -676,7 +677,7 @@ func (com *Compiler) compileArgWhere(sel *Select, arg *Arg, role string) (error,
|
||||
if nu && role == "anon" {
|
||||
sel.SkipRender = true
|
||||
}
|
||||
addFilter(sel, ex)
|
||||
AddFilter(sel, ex)
|
||||
|
||||
return nil, true
|
||||
}
|
||||
@ -820,7 +821,7 @@ func (com *Compiler) getRole(role, field string) *trval {
|
||||
}
|
||||
}
|
||||
|
||||
func addFilter(sel *Select, fil *Exp) {
|
||||
func AddFilter(sel *Select, fil *Exp) {
|
||||
if sel.Where != nil {
|
||||
ow := sel.Where
|
||||
|
||||
@ -937,6 +938,12 @@ func newExp(st *util.Stack, node *Node, usePool bool) (*Exp, error) {
|
||||
case "is_null":
|
||||
ex.Op = OpIsNull
|
||||
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:
|
||||
pushChildren(st, node.exp, node)
|
||||
return nil, nil // skip node
|
||||
|
@ -39,11 +39,16 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
|
||||
|
||||
}
|
||||
v := fields[0].Value
|
||||
|
||||
// Open and close quotes
|
||||
if len(v) >= 2 && v[0] == '"' && v[len(v)-1] == '"' {
|
||||
fields[0].Value = v[1 : len(v)-1]
|
||||
}
|
||||
|
||||
if tag == "cursor" {
|
||||
if bytes.EqualFold(v, []byte("null")) {
|
||||
return io.WriteString(w, ``)
|
||||
}
|
||||
v1, err := decrypt(string(fields[0].Value))
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
fmt.Println(">>>", tag, string(v))
|
||||
|
||||
return w.Write(escQuote(fields[0].Value))
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,13 @@ func cmdServ(cmd *cobra.Command, args []string) {
|
||||
fatalInProd(err, "failed to connect to database")
|
||||
}
|
||||
|
||||
initCrypto()
|
||||
initCompiler()
|
||||
initResolvers()
|
||||
initAllowList(confPath)
|
||||
initPreparedList(confPath)
|
||||
if conf != nil && db != nil {
|
||||
initCrypto()
|
||||
initCompiler()
|
||||
initResolvers()
|
||||
initAllowList(confPath)
|
||||
initPreparedList(confPath)
|
||||
}
|
||||
|
||||
startHTTP()
|
||||
}
|
||||
|
@ -36,15 +36,20 @@ func encryptCursor(qc *qcode.QCode, data []byte) ([]byte, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
v, err := crypto.Encrypt(f.Value[1:len(f.Value)-1], &internalKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
buf.WriteByte('"')
|
||||
buf.WriteString(base64.StdEncoding.EncodeToString(v))
|
||||
buf.WriteByte('"')
|
||||
|
||||
if len(f.Value) > 2 {
|
||||
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()
|
||||
}
|
||||
|
13
serv/init.go
13
serv/init.go
@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/dosco/super-graph/allow"
|
||||
"github.com/dosco/super-graph/crypto"
|
||||
@ -135,7 +136,17 @@ func initDBPool(c *config) (*pgxpool.Pool, error) {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
|
502
serv/rice-box.go
502
serv/rice-box.go
File diff suppressed because one or more lines are too long
@ -109,7 +109,7 @@ database:
|
||||
port: 5432
|
||||
dbname: {% app_name_slug %}_development
|
||||
user: postgres
|
||||
password: ''
|
||||
password: postgres
|
||||
|
||||
#schema: "public"
|
||||
#pool_size: 10
|
||||
|
@ -2,7 +2,10 @@ version: '3.4'
|
||||
services:
|
||||
# Postgres DB
|
||||
db:
|
||||
image: postgres:latest
|
||||
image: postgres:12
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
|
@ -54,7 +54,7 @@ database:
|
||||
port: 5432
|
||||
dbname: {% app_name_slug %}_development
|
||||
user: postgres
|
||||
password: ''
|
||||
password: postgres
|
||||
#pool_size: 10
|
||||
#max_retries: 0
|
||||
#log_level: "debug"
|
||||
|
Reference in New Issue
Block a user