Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
52f3b1c7a2 | |||
2d466bfb12 |
@ -249,7 +249,7 @@ func nestedInsertOneToManyWithConnect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (INSERT INTO "users" ("full_name", "email", "created_at", "updated_at") SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t RETURNING *), "products" AS ( UPDATE "products" SET "user_id" = "users"."id"FROM "users" WHERE ("products"."id" = '5') RETURNING "products".*) SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (INSERT INTO "users" ("full_name", "email", "created_at", "updated_at") SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t RETURNING *), "products" AS ( UPDATE "products" SET "user_id" = "users"."id" FROM "users" WHERE ("products"."id"= ((i.j->'product'->'connect'->>'id'))::bigint) RETURNING "products".*) SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
@ -278,6 +278,10 @@ func nestedInsertOneToOneWithConnect(t *testing.T) {
|
|||||||
product(insert: $data) {
|
product(insert: $data) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
tags {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
full_name
|
full_name
|
||||||
@ -286,7 +290,7 @@ func nestedInsertOneToOneWithConnect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT * FROM "users" WHERE "users"."id" = '5' LIMIT 1), "products" AS (INSERT INTO "products" ("name", "price", "created_at", "updated_at", "user_id") SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT "id" FROM "_sg_input" i,"users" WHERE "users"."id"= ((i.j->'user'->'connect'->>'id'))::bigint LIMIT 1), "products" AS (INSERT INTO "products" ("name", "price", "created_at", "updated_at", "user_id") SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user", "tags_2_join"."json_2" AS "tags") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id", "products"."tags" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg("json_2"), '[]') AS "json_2" FROM (SELECT row_to_json((SELECT "json_row_2" FROM (SELECT "tags_2"."id" AS "id", "tags_2"."name" AS "name") AS "json_row_2")) AS "json_2" FROM (SELECT "tags"."id", "tags"."name" FROM "tags" WHERE ((("tags"."slug") = any ("products_0"."tags"))) LIMIT ('20') :: integer) AS "tags_2" LIMIT ('20') :: integer) AS "json_agg_2") AS "tags_2_join" ON ('true') LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
@ -310,6 +314,43 @@ func nestedInsertOneToOneWithConnect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func nestedInsertOneToOneWithConnectArray(t *testing.T) {
|
||||||
|
gql := `mutation {
|
||||||
|
product(insert: $data) {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user {
|
||||||
|
id
|
||||||
|
full_name
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT "id" FROM "_sg_input" i,"users" WHERE "users"."id" = ANY((select a::bigint AS list from json_array_elements_text((i.j->'user'->'connect'->>'id')::json) AS a)) LIMIT 1), "products" AS (INSERT INTO "products" ("name", "price", "created_at", "updated_at", "user_id") SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t RETURNING *) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
|
vars := map[string]json.RawMessage{
|
||||||
|
"data": json.RawMessage(`{
|
||||||
|
"name": "Apple",
|
||||||
|
"price": 1.25,
|
||||||
|
"created_at": "now",
|
||||||
|
"updated_at": "now",
|
||||||
|
"user": {
|
||||||
|
"connect": { "id": [1,2] }
|
||||||
|
}
|
||||||
|
}`),
|
||||||
|
}
|
||||||
|
|
||||||
|
resSQL, err := compileGQLToPSQL(gql, vars, "admin")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(resSQL) != sql {
|
||||||
|
t.Fatal(errNotExpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCompileInsert(t *testing.T) {
|
func TestCompileInsert(t *testing.T) {
|
||||||
t.Run("simpleInsert", simpleInsert)
|
t.Run("simpleInsert", simpleInsert)
|
||||||
t.Run("singleInsert", singleInsert)
|
t.Run("singleInsert", singleInsert)
|
||||||
@ -320,4 +361,6 @@ func TestCompileInsert(t *testing.T) {
|
|||||||
t.Run("nestedInsertOneToOne", nestedInsertOneToOne)
|
t.Run("nestedInsertOneToOne", nestedInsertOneToOne)
|
||||||
t.Run("nestedInsertOneToManyWithConnect", nestedInsertOneToManyWithConnect)
|
t.Run("nestedInsertOneToManyWithConnect", nestedInsertOneToManyWithConnect)
|
||||||
t.Run("nestedInsertOneToOneWithConnect", nestedInsertOneToOneWithConnect)
|
t.Run("nestedInsertOneToOneWithConnect", nestedInsertOneToOneWithConnect)
|
||||||
|
t.Run("nestedInsertOneToOneWithConnectArray", nestedInsertOneToOneWithConnectArray)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
144
psql/mutate.go
144
psql/mutate.go
@ -101,6 +101,9 @@ type renitem struct {
|
|||||||
data map[string]json.RawMessage
|
data map[string]json.RawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Handle cases where a column name matches the child table name
|
||||||
|
// the child path needs to be exluded in the json sent to insert or update
|
||||||
|
|
||||||
func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
||||||
var data map[string]json.RawMessage
|
var data map[string]json.RawMessage
|
||||||
var array bool
|
var array bool
|
||||||
@ -124,9 +127,6 @@ func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
|||||||
if v[0] != '{' && v[0] != '[' {
|
if v[0] != '{' && v[0] != '[' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if _, ok := item.ti.ColMap[k]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get child-to-parent relationship
|
// Get child-to-parent relationship
|
||||||
relCP, err := c.schema.GetRel(k, item.key)
|
relCP, err := c.schema.GetRel(k, item.key)
|
||||||
@ -152,13 +152,9 @@ func (c *compilerContext) handleKVItem(st *util.Stack, item kvitem) error {
|
|||||||
id++
|
id++
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
ti, err := c.schema.GetTable(k)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// Get parent-to-child relationship
|
// Get parent-to-child relationship
|
||||||
relPC, err := c.schema.GetRel(item.key, k)
|
} else if relPC, err := c.schema.GetRel(item.key, k); err == nil {
|
||||||
|
ti, err := c.schema.GetTable(k)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -277,8 +273,12 @@ func (c *compilerContext) renderUnionStmt(w io.Writer, item renitem) error {
|
|||||||
io.WriteString(w, ` SET `)
|
io.WriteString(w, ` SET `)
|
||||||
quoted(w, item.relPC.Right.Col)
|
quoted(w, item.relPC.Right.Col)
|
||||||
io.WriteString(w, ` = `)
|
io.WriteString(w, ` = `)
|
||||||
|
|
||||||
|
// When setting the id of the connected table in a one-to-many setting
|
||||||
|
// we always overwrite the value including for array columns
|
||||||
colWithTable(w, item.relPC.Left.Table, item.relPC.Left.Col)
|
colWithTable(w, item.relPC.Left.Table, item.relPC.Left.Col)
|
||||||
io.WriteString(w, `FROM `)
|
|
||||||
|
io.WriteString(w, ` FROM `)
|
||||||
quoted(w, item.relPC.Left.Table)
|
quoted(w, item.relPC.Left.Table)
|
||||||
io.WriteString(w, ` WHERE`)
|
io.WriteString(w, ` WHERE`)
|
||||||
|
|
||||||
@ -290,7 +290,7 @@ func (c *compilerContext) renderUnionStmt(w io.Writer, item renitem) error {
|
|||||||
} else {
|
} else {
|
||||||
io.WriteString(w, ` (`)
|
io.WriteString(w, ` (`)
|
||||||
}
|
}
|
||||||
if err := renderKVItemWhere(w, v); err != nil {
|
if err := renderWhereFromJSON(w, v, "connect", v.val); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
io.WriteString(w, `)`)
|
io.WriteString(w, `)`)
|
||||||
@ -313,7 +313,19 @@ func (c *compilerContext) renderUnionStmt(w io.Writer, item renitem) error {
|
|||||||
quoted(w, item.ti.Name)
|
quoted(w, item.ti.Name)
|
||||||
io.WriteString(w, ` SET `)
|
io.WriteString(w, ` SET `)
|
||||||
quoted(w, item.relPC.Right.Col)
|
quoted(w, item.relPC.Right.Col)
|
||||||
io.WriteString(w, ` = NULL`)
|
io.WriteString(w, ` = `)
|
||||||
|
|
||||||
|
if item.relPC.Right.Array {
|
||||||
|
io.WriteString(w, ` array_remove(`)
|
||||||
|
quoted(w, item.relPC.Right.Col)
|
||||||
|
io.WriteString(w, `, `)
|
||||||
|
colWithTable(w, item.relPC.Left.Table, item.relPC.Left.Col)
|
||||||
|
io.WriteString(w, `)`)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
io.WriteString(w, ` NULL`)
|
||||||
|
}
|
||||||
|
|
||||||
io.WriteString(w, ` FROM `)
|
io.WriteString(w, ` FROM `)
|
||||||
quoted(w, item.relPC.Left.Table)
|
quoted(w, item.relPC.Left.Table)
|
||||||
io.WriteString(w, ` WHERE`)
|
io.WriteString(w, ` WHERE`)
|
||||||
@ -326,7 +338,7 @@ func (c *compilerContext) renderUnionStmt(w io.Writer, item renitem) error {
|
|||||||
} else {
|
} else {
|
||||||
io.WriteString(w, ` (`)
|
io.WriteString(w, ` (`)
|
||||||
}
|
}
|
||||||
if err := renderKVItemWhere(w, v); err != nil {
|
if err := renderWhereFromJSON(w, v, "disconnect", v.val); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
io.WriteString(w, `)`)
|
io.WriteString(w, `)`)
|
||||||
@ -514,12 +526,24 @@ func (c *compilerContext) renderConnectStmt(qc *qcode.QCode, w io.Writer,
|
|||||||
|
|
||||||
io.WriteString(w, `, `)
|
io.WriteString(w, `, `)
|
||||||
quoted(w, item.ti.Name)
|
quoted(w, item.ti.Name)
|
||||||
io.WriteString(c.w, ` AS (`)
|
io.WriteString(c.w, ` AS (SELECT `)
|
||||||
|
|
||||||
io.WriteString(c.w, `SELECT * FROM `)
|
if rel.Left.Array {
|
||||||
|
io.WriteString(w, `array_agg(DISTINCT `)
|
||||||
|
quoted(w, rel.Right.Col)
|
||||||
|
io.WriteString(w, `) AS `)
|
||||||
|
quoted(w, rel.Right.Col)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
quoted(w, rel.Right.Col)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
io.WriteString(c.w, ` FROM "_sg_input" i,`)
|
||||||
quoted(c.w, item.ti.Name)
|
quoted(c.w, item.ti.Name)
|
||||||
|
|
||||||
io.WriteString(c.w, ` WHERE `)
|
io.WriteString(c.w, ` WHERE `)
|
||||||
if err := renderKVItemWhere(c.w, item.kvitem); err != nil {
|
if err := renderWhereFromJSON(c.w, item.kvitem, "connect", item.kvitem.val); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
io.WriteString(c.w, ` LIMIT 1)`)
|
io.WriteString(c.w, ` LIMIT 1)`)
|
||||||
@ -540,43 +564,95 @@ func (c *compilerContext) renderDisconnectStmt(qc *qcode.QCode, w io.Writer,
|
|||||||
quoted(w, item.ti.Name)
|
quoted(w, item.ti.Name)
|
||||||
io.WriteString(c.w, ` AS (`)
|
io.WriteString(c.w, ` AS (`)
|
||||||
|
|
||||||
io.WriteString(c.w, `SELECT * FROM (VALUES(NULL::`)
|
if rel.Right.Array {
|
||||||
io.WriteString(w, rel.Right.col.Type)
|
io.WriteString(c.w, `SELECT `)
|
||||||
io.WriteString(c.w, `)) AS LOOKUP(`)
|
quoted(w, rel.Right.Col)
|
||||||
quoted(w, rel.Right.Col)
|
io.WriteString(c.w, ` FROM "_sg_input" i,`)
|
||||||
io.WriteString(c.w, `))`)
|
quoted(c.w, item.ti.Name)
|
||||||
|
io.WriteString(c.w, ` WHERE `)
|
||||||
|
if err := renderWhereFromJSON(c.w, item.kvitem, "connect", item.kvitem.val); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
io.WriteString(c.w, ` LIMIT 1))`)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
io.WriteString(c.w, `SELECT * FROM (VALUES(NULL::`)
|
||||||
|
io.WriteString(w, rel.Right.col.Type)
|
||||||
|
io.WriteString(c.w, `)) AS LOOKUP(`)
|
||||||
|
quoted(w, rel.Right.Col)
|
||||||
|
io.WriteString(c.w, `))`)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderKVItemWhere(w io.Writer, item kvitem) error {
|
func renderWhereFromJSON(w io.Writer, item kvitem, key string, val []byte) error {
|
||||||
return renderWhereFromJSON(w, item.ti.Name, item.val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func renderWhereFromJSON(w io.Writer, table string, val []byte) error {
|
|
||||||
var kv map[string]json.RawMessage
|
var kv map[string]json.RawMessage
|
||||||
|
ti := item.ti
|
||||||
|
|
||||||
if err := json.Unmarshal(val, &kv); err != nil {
|
if err := json.Unmarshal(val, &kv); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i := 0
|
i := 0
|
||||||
for k, v := range kv {
|
for k, v := range kv {
|
||||||
|
col, ok := ti.ColMap[k]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
io.WriteString(w, ` AND `)
|
io.WriteString(w, ` AND `)
|
||||||
}
|
}
|
||||||
colWithTable(w, table, k)
|
|
||||||
io.WriteString(w, ` = '`)
|
if v[0] == '[' {
|
||||||
switch v[0] {
|
colWithTable(w, ti.Name, k)
|
||||||
case '"':
|
|
||||||
w.Write(v[1 : len(v)-1])
|
if col.Array {
|
||||||
default:
|
io.WriteString(w, ` && `)
|
||||||
w.Write(v)
|
} else {
|
||||||
|
io.WriteString(w, ` = `)
|
||||||
|
}
|
||||||
|
|
||||||
|
io.WriteString(w, `ANY((select a::`)
|
||||||
|
io.WriteString(w, col.Type)
|
||||||
|
|
||||||
|
io.WriteString(w, ` AS list from json_array_elements_text(`)
|
||||||
|
renderPathJSON(w, item, key, k)
|
||||||
|
io.WriteString(w, `::json) AS a))`)
|
||||||
|
|
||||||
|
} else if col.Array {
|
||||||
|
io.WriteString(w, `(`)
|
||||||
|
renderPathJSON(w, item, key, k)
|
||||||
|
io.WriteString(w, `)::`)
|
||||||
|
io.WriteString(w, col.Type)
|
||||||
|
|
||||||
|
io.WriteString(w, ` = ANY(`)
|
||||||
|
colWithTable(w, ti.Name, k)
|
||||||
|
io.WriteString(w, `)`)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
colWithTable(w, ti.Name, k)
|
||||||
|
|
||||||
|
io.WriteString(w, `= (`)
|
||||||
|
renderPathJSON(w, item, key, k)
|
||||||
|
io.WriteString(w, `)::`)
|
||||||
|
io.WriteString(w, col.Type)
|
||||||
}
|
}
|
||||||
io.WriteString(w, `'`)
|
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func renderPathJSON(w io.Writer, item kvitem, key1, key2 string) {
|
||||||
|
io.WriteString(w, `(i.j->`)
|
||||||
|
joinPath(w, item.path)
|
||||||
|
io.WriteString(w, `->'`)
|
||||||
|
io.WriteString(w, key1)
|
||||||
|
io.WriteString(w, `'->>'`)
|
||||||
|
io.WriteString(w, key2)
|
||||||
|
io.WriteString(w, `')`)
|
||||||
|
}
|
||||||
|
|
||||||
func renderCteName(w io.Writer, item kvitem) error {
|
func renderCteName(w io.Writer, item kvitem) error {
|
||||||
io.WriteString(w, `"`)
|
io.WriteString(w, `"`)
|
||||||
io.WriteString(w, item.ti.Name)
|
io.WriteString(w, item.ti.Name)
|
||||||
|
@ -82,17 +82,21 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer) (uint32, error) {
|
|||||||
multiRoot := (len(qc.Roots) > 1)
|
multiRoot := (len(qc.Roots) > 1)
|
||||||
|
|
||||||
st := NewIntStack()
|
st := NewIntStack()
|
||||||
|
si := 0
|
||||||
|
|
||||||
if multiRoot {
|
if multiRoot {
|
||||||
io.WriteString(c.w, `SELECT row_to_json("json_root") FROM (SELECT `)
|
io.WriteString(c.w, `SELECT row_to_json("json_root") FROM (SELECT `)
|
||||||
|
|
||||||
for i, id := range qc.Roots {
|
for _, id := range qc.Roots {
|
||||||
root := qc.Selects[id]
|
root := qc.Selects[id]
|
||||||
|
if root.SkipRender {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
st.Push(root.ID + closeBlock)
|
st.Push(root.ID + closeBlock)
|
||||||
st.Push(root.ID)
|
st.Push(root.ID)
|
||||||
|
|
||||||
if i != 0 {
|
if si != 0 {
|
||||||
io.WriteString(c.w, `, `)
|
io.WriteString(c.w, `, `)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,24 +107,34 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer) (uint32, error) {
|
|||||||
io.WriteString(c.w, `"`)
|
io.WriteString(c.w, `"`)
|
||||||
|
|
||||||
alias(c.w, root.FieldName)
|
alias(c.w, root.FieldName)
|
||||||
|
si++
|
||||||
}
|
}
|
||||||
|
|
||||||
io.WriteString(c.w, ` FROM `)
|
if si != 0 {
|
||||||
|
io.WriteString(c.w, ` FROM `)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
root := qc.Selects[0]
|
root := qc.Selects[0]
|
||||||
|
if !root.SkipRender {
|
||||||
|
io.WriteString(c.w, `SELECT json_object_agg(`)
|
||||||
|
io.WriteString(c.w, `'`)
|
||||||
|
io.WriteString(c.w, root.FieldName)
|
||||||
|
io.WriteString(c.w, `', `)
|
||||||
|
io.WriteString(c.w, `json_`)
|
||||||
|
int2string(c.w, root.ID)
|
||||||
|
|
||||||
io.WriteString(c.w, `SELECT json_object_agg(`)
|
st.Push(root.ID + closeBlock)
|
||||||
io.WriteString(c.w, `'`)
|
st.Push(root.ID)
|
||||||
io.WriteString(c.w, root.FieldName)
|
|
||||||
io.WriteString(c.w, `', `)
|
|
||||||
io.WriteString(c.w, `json_`)
|
|
||||||
int2string(c.w, root.ID)
|
|
||||||
|
|
||||||
st.Push(root.ID + closeBlock)
|
io.WriteString(c.w, `) FROM `)
|
||||||
st.Push(root.ID)
|
si++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
io.WriteString(c.w, `) FROM `)
|
if si == 0 {
|
||||||
|
return 0, errors.New("all tables skipped. cannot render query")
|
||||||
}
|
}
|
||||||
|
|
||||||
var ignored uint32
|
var ignored uint32
|
||||||
@ -161,6 +175,9 @@ func (co *Compiler) compileQuery(qc *qcode.QCode, w io.Writer) (uint32, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
child := &c.s[cid]
|
child := &c.s[cid]
|
||||||
|
if child.SkipRender {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
st.Push(child.ID + closeBlock)
|
st.Push(child.ID + closeBlock)
|
||||||
st.Push(child.ID)
|
st.Push(child.ID)
|
||||||
@ -475,18 +492,22 @@ func (c *compilerContext) renderRemoteRelColumns(sel *qcode.Select, ti *DBTableI
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) renderJoinedColumns(sel *qcode.Select, ti *DBTableInfo, skipped uint32) error {
|
func (c *compilerContext) renderJoinedColumns(sel *qcode.Select, ti *DBTableInfo, skipped uint32) error {
|
||||||
colsRendered := len(sel.Cols) != 0
|
|
||||||
|
// columns previously rendered
|
||||||
|
i := len(sel.Cols)
|
||||||
|
|
||||||
for _, id := range sel.Children {
|
for _, id := range sel.Children {
|
||||||
skipThis := hasBit(skipped, uint32(id))
|
if hasBit(skipped, uint32(id)) {
|
||||||
|
|
||||||
if colsRendered && !skipThis {
|
|
||||||
io.WriteString(c.w, ", ")
|
|
||||||
}
|
|
||||||
if skipThis {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
childSel := &c.s[id]
|
childSel := &c.s[id]
|
||||||
|
if childSel.SkipRender {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if i != 0 {
|
||||||
|
io.WriteString(c.w, ", ")
|
||||||
|
}
|
||||||
|
|
||||||
//fmt.Fprintf(w, `"%s_%d_join"."%s" AS "%s"`,
|
//fmt.Fprintf(w, `"%s_%d_join"."%s" AS "%s"`,
|
||||||
//s.Name, s.ID, s.Name, s.FieldName)
|
//s.Name, s.ID, s.Name, s.FieldName)
|
||||||
@ -500,6 +521,7 @@ func (c *compilerContext) renderJoinedColumns(sel *qcode.Select, ti *DBTableInfo
|
|||||||
io.WriteString(c.w, `" AS "`)
|
io.WriteString(c.w, `" AS "`)
|
||||||
io.WriteString(c.w, childSel.FieldName)
|
io.WriteString(c.w, childSel.FieldName)
|
||||||
io.WriteString(c.w, `"`)
|
io.WriteString(c.w, `"`)
|
||||||
|
i++
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -632,10 +654,6 @@ func (c *compilerContext) renderBaseSelect(sel *qcode.Select, ti *DBTableInfo,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if i != 0 && len(sel.OrderBy) != 0 {
|
|
||||||
// io.WriteString(c.w, ", ")
|
|
||||||
// }
|
|
||||||
|
|
||||||
for _, ob := range sel.OrderBy {
|
for _, ob := range sel.OrderBy {
|
||||||
if _, ok := colmap[ob.Col]; ok {
|
if _, ok := colmap[ob.Col]; ok {
|
||||||
continue
|
continue
|
||||||
|
@ -463,6 +463,30 @@ func multiRoot(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func skipUserIDForAnonRole(t *testing.T) {
|
||||||
|
gql := `query {
|
||||||
|
products {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
user(where: { id: { eq: $user_id } }) {
|
||||||
|
id
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
sql := `SELECT json_object_agg('products', json_0) FROM (SELECT coalesce(json_agg("json_0"), '[]') AS "json_0" FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('20') :: integer) AS "products_0" LIMIT ('20') :: integer) AS "json_agg_0") AS "sel_0"`
|
||||||
|
|
||||||
|
resSQL, err := compileGQLToPSQL(gql, nil, "anon")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if string(resSQL) != sql {
|
||||||
|
t.Fatal(errNotExpected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func blockedQuery(t *testing.T) {
|
func blockedQuery(t *testing.T) {
|
||||||
gql := `query {
|
gql := `query {
|
||||||
user(id: 5, where: { id: { gt: 3 } }) {
|
user(id: 5, where: { id: { gt: 3 } }) {
|
||||||
@ -524,6 +548,7 @@ func TestCompileQuery(t *testing.T) {
|
|||||||
t.Run("queryWithVariables", queryWithVariables)
|
t.Run("queryWithVariables", queryWithVariables)
|
||||||
t.Run("withWhereOnRelations", withWhereOnRelations)
|
t.Run("withWhereOnRelations", withWhereOnRelations)
|
||||||
t.Run("multiRoot", multiRoot)
|
t.Run("multiRoot", multiRoot)
|
||||||
|
t.Run("skipUserIDForAnonRole", skipUserIDForAnonRole)
|
||||||
t.Run("blockedQuery", blockedQuery)
|
t.Run("blockedQuery", blockedQuery)
|
||||||
t.Run("blockedFunctions", blockedFunctions)
|
t.Run("blockedFunctions", blockedFunctions)
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,9 @@ func (s *DBSchema) updateRelationships(t DBTable, cols []DBColumn) error {
|
|||||||
return fmt.Errorf("invalid foreign key table '%s'", ct)
|
return fmt.Errorf("invalid foreign key table '%s'", ct)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cols {
|
for i := range cols {
|
||||||
|
c := cols[i]
|
||||||
|
|
||||||
if len(c.FKeyTable) == 0 || len(c.FKeyColID) == 0 {
|
if len(c.FKeyTable) == 0 || len(c.FKeyColID) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -188,10 +190,12 @@ func (s *DBSchema) updateRelationships(t DBTable, cols []DBColumn) error {
|
|||||||
rel2 = &DBRel{Type: RelOneToMany}
|
rel2 = &DBRel{Type: RelOneToMany}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rel2.Left.col = fc
|
||||||
rel2.Left.Table = c.FKeyTable
|
rel2.Left.Table = c.FKeyTable
|
||||||
rel2.Left.Col = fc.Name
|
rel2.Left.Col = fc.Name
|
||||||
rel2.Left.Array = fc.Array
|
rel2.Left.Array = fc.Array
|
||||||
|
|
||||||
|
rel2.Right.col = &c
|
||||||
rel2.Right.Table = t.Name
|
rel2.Right.Table = t.Name
|
||||||
rel2.Right.Col = c.Name
|
rel2.Right.Col = c.Name
|
||||||
rel2.Right.Array = c.Array
|
rel2.Right.Array = c.Array
|
||||||
@ -249,9 +253,11 @@ func (s *DBSchema) updateSchemaOTMT(
|
|||||||
rel1.Through = ti.Name
|
rel1.Through = ti.Name
|
||||||
rel1.ColT = col2.Name
|
rel1.ColT = col2.Name
|
||||||
|
|
||||||
|
rel1.Left.col = &col2
|
||||||
rel1.Left.Table = col2.FKeyTable
|
rel1.Left.Table = col2.FKeyTable
|
||||||
rel1.Left.Col = fc2.Name
|
rel1.Left.Col = fc2.Name
|
||||||
|
|
||||||
|
rel1.Right.col = &col1
|
||||||
rel1.Right.Table = ti.Name
|
rel1.Right.Table = ti.Name
|
||||||
rel1.Right.Col = col1.Name
|
rel1.Right.Col = col1.Name
|
||||||
|
|
||||||
@ -265,9 +271,11 @@ func (s *DBSchema) updateSchemaOTMT(
|
|||||||
rel2.Through = ti.Name
|
rel2.Through = ti.Name
|
||||||
rel2.ColT = col1.Name
|
rel2.ColT = col1.Name
|
||||||
|
|
||||||
|
rel1.Left.col = fc1
|
||||||
rel2.Left.Table = col1.FKeyTable
|
rel2.Left.Table = col1.FKeyTable
|
||||||
rel2.Left.Col = fc1.Name
|
rel2.Left.Col = fc1.Name
|
||||||
|
|
||||||
|
rel1.Right.col = &col2
|
||||||
rel2.Right.Table = ti.Name
|
rel2.Right.Table = ti.Name
|
||||||
rel2.Right.Col = col2.Name
|
rel2.Right.Col = col2.Name
|
||||||
|
|
||||||
|
@ -125,10 +125,10 @@ func (c *compilerContext) renderUpdateStmt(w io.Writer, qc *qcode.QCode, item re
|
|||||||
if item.relPC.Type == RelOneToMany {
|
if item.relPC.Type == RelOneToMany {
|
||||||
if conn, ok := item.data["where"]; ok {
|
if conn, ok := item.data["where"]; ok {
|
||||||
io.WriteString(w, ` AND `)
|
io.WriteString(w, ` AND `)
|
||||||
renderWhereFromJSON(w, item.ti.Name, conn)
|
renderWhereFromJSON(w, item.kvitem, "where", conn)
|
||||||
} else if conn, ok := item.data["_where"]; ok {
|
} else if conn, ok := item.data["_where"]; ok {
|
||||||
io.WriteString(w, ` AND `)
|
io.WriteString(w, ` AND `)
|
||||||
renderWhereFromJSON(w, item.ti.Name, conn)
|
renderWhereFromJSON(w, item.kvitem, "_where", conn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
io.WriteString(w, `)`)
|
io.WriteString(w, `)`)
|
||||||
@ -167,7 +167,15 @@ func renderNestedUpdateRelColumns(w io.Writer, item kvitem, values bool) error {
|
|||||||
if values {
|
if values {
|
||||||
colWithTable(w, v.relCP.Left.Table, v.relCP.Left.Col)
|
colWithTable(w, v.relCP.Left.Table, v.relCP.Left.Col)
|
||||||
} else {
|
} else {
|
||||||
quoted(w, v.relCP.Right.Col)
|
if v.relCP.Right.Array {
|
||||||
|
io.WriteString(w, `array_remove(`)
|
||||||
|
colWithTable(w, v.relCP.Left.Table, v.relCP.Left.Col)
|
||||||
|
io.WriteString(w, `, `)
|
||||||
|
quoted(w, v.relCP.Right.Col)
|
||||||
|
io.WriteString(w, `)`)
|
||||||
|
} else {
|
||||||
|
quoted(w, v.relCP.Right.Col)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -119,7 +119,7 @@ func nestedUpdateOneToMany(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t) WHERE (("users"."id") IS NOT DISTINCT FROM 8) RETURNING "users".*), "products" AS (UPDATE "products" SET ("name", "price", "created_at", "updated_at") = (SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::products, i.j->'product') t) FROM "users" WHERE (("products"."user_id") = ("users"."id") AND "products"."id" = '2') RETURNING "products".*) SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t) WHERE (("users"."id") IS NOT DISTINCT FROM 8) RETURNING "users".*), "products" AS (UPDATE "products" SET ("name", "price", "created_at", "updated_at") = (SELECT "t"."name", "t"."price", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::products, i.j->'product') t) FROM "users" WHERE (("products"."user_id") = ("users"."id") AND "products"."id"= ((i.j->'product'->'where'->>'id'))::bigint) RETURNING "products".*) SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
@ -200,7 +200,7 @@ func nestedUpdateOneToManyWithConnect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql1 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t) WHERE (("users"."id") = 6) RETURNING "users".*), "products_c" AS ( UPDATE "products" SET "user_id" = "users"."id"FROM "users" WHERE ("products"."id" = '7') RETURNING "products".*), "products_d" AS ( UPDATE "products" SET "user_id" = NULL FROM "users" WHERE ("products"."id" = '8') RETURNING "products".*), "products" AS (SELECT * FROM "products_c" UNION ALL SELECT * FROM "products_d") SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql1 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (UPDATE "users" SET ("full_name", "email", "created_at", "updated_at") = (SELECT "t"."full_name", "t"."email", "t"."created_at", "t"."updated_at" FROM "_sg_input" i, json_populate_record(NULL::users, i.j) t) WHERE (("users"."id") = 6) RETURNING "users".*), "products_c" AS ( UPDATE "products" SET "user_id" = "users"."id" FROM "users" WHERE ("products"."id"= ((i.j->'product'->'connect'->>'id'))::bigint) RETURNING "products".*), "products_d" AS ( UPDATE "products" SET "user_id" = NULL FROM "users" WHERE ("products"."id"= ((i.j->'product'->'disconnect'->>'id'))::bigint) RETURNING "products".*), "products" AS (SELECT * FROM "products_c" UNION ALL SELECT * FROM "products_d") SELECT json_object_agg('user', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."full_name" AS "full_name", "users_0"."email" AS "email", "product_1_join"."json_1" AS "product") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" LIMIT ('1') :: integer) AS "users_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "products_1"."id" AS "id", "products_1"."name" AS "name", "products_1"."price" AS "price") AS "json_row_1")) AS "json_1" FROM (SELECT "products"."id", "products"."name", "products"."price" FROM "products" WHERE ((("products"."user_id") = ("users_0"."id"))) LIMIT ('1') :: integer) AS "products_1" LIMIT ('1') :: integer) AS "product_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
@ -238,9 +238,9 @@ func nestedUpdateOneToOneWithConnect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
sql1 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT * FROM "users" WHERE "users"."id" = '5' AND "users"."email" = 'test@test.com' LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT "t"."name", "t"."price", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t) WHERE (("products"."id") = 9) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql1 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT "id" FROM "_sg_input" i,"users" WHERE "users"."id"= ((i.j->'user'->'connect'->>'id'))::bigint AND "users"."email"= ((i.j->'user'->'connect'->>'email'))::character varying LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT "t"."name", "t"."price", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t) WHERE (("products"."id") = 9) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
sql2 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT * FROM "users" WHERE "users"."email" = 'test@test.com' AND "users"."id" = '5' LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT "t"."name", "t"."price", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t) WHERE (("products"."id") = 9) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
sql2 := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT "id" FROM "_sg_input" i,"users" WHERE "users"."email"= ((i.j->'user'->'connect'->>'email'))::character varying AND "users"."id"= ((i.j->'user'->'connect'->>'id'))::bigint LIMIT 1), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT "t"."name", "t"."price", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t) WHERE (("products"."id") = 9) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "user_1_join"."json_1" AS "user") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LEFT OUTER JOIN LATERAL (SELECT row_to_json((SELECT "json_row_1" FROM (SELECT "users_1"."id" AS "id", "users_1"."full_name" AS "full_name", "users_1"."email" AS "email") AS "json_row_1")) AS "json_1" FROM (SELECT "users"."id", "users"."full_name", "users"."email" FROM "users" WHERE ((("users"."id") = ("products_0"."user_id"))) LIMIT ('1') :: integer) AS "users_1" LIMIT ('1') :: integer) AS "user_1_join" ON ('true') LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
vars := map[string]json.RawMessage{
|
vars := map[string]json.RawMessage{
|
||||||
"data": json.RawMessage(`{
|
"data": json.RawMessage(`{
|
||||||
@ -295,6 +295,37 @@ func nestedUpdateOneToOneWithDisconnect(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// func nestedUpdateOneToOneWithDisconnectArray(t *testing.T) {
|
||||||
|
// gql := `mutation {
|
||||||
|
// product(update: $data, id: 2) {
|
||||||
|
// id
|
||||||
|
// name
|
||||||
|
// user_id
|
||||||
|
// }
|
||||||
|
// }`
|
||||||
|
|
||||||
|
// sql := `WITH "_sg_input" AS (SELECT '{{data}}' :: json AS j), "users" AS (SELECT * FROM (VALUES(NULL::bigint)) AS LOOKUP("id")), "products" AS (UPDATE "products" SET ("name", "price", "user_id") = (SELECT "t"."name", "t"."price", "users"."id" FROM "_sg_input" i, "users", json_populate_record(NULL::products, i.j) t) WHERE (("products"."id") = 2) RETURNING "products".*) SELECT json_object_agg('product', json_0) FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "products_0"."id" AS "id", "products_0"."name" AS "name", "products_0"."user_id" AS "user_id") AS "json_row_0")) AS "json_0" FROM (SELECT "products"."id", "products"."name", "products"."user_id" FROM "products" LIMIT ('1') :: integer) AS "products_0" LIMIT ('1') :: integer) AS "sel_0"`
|
||||||
|
|
||||||
|
// vars := map[string]json.RawMessage{
|
||||||
|
// "data": json.RawMessage(`{
|
||||||
|
// "name": "Apple",
|
||||||
|
// "price": 1.25,
|
||||||
|
// "user": {
|
||||||
|
// "disconnect": { "id": 5 }
|
||||||
|
// }
|
||||||
|
// }`),
|
||||||
|
// }
|
||||||
|
|
||||||
|
// resSQL, err := compileGQLToPSQL(gql, vars, "admin")
|
||||||
|
// if err != nil {
|
||||||
|
// t.Fatal(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if string(resSQL) != sql {
|
||||||
|
// t.Fatal(errNotExpected)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
func TestCompileUpdate(t *testing.T) {
|
func TestCompileUpdate(t *testing.T) {
|
||||||
t.Run("singleUpdate", singleUpdate)
|
t.Run("singleUpdate", singleUpdate)
|
||||||
t.Run("simpleUpdateWithPresets", simpleUpdateWithPresets)
|
t.Run("simpleUpdateWithPresets", simpleUpdateWithPresets)
|
||||||
@ -304,4 +335,5 @@ func TestCompileUpdate(t *testing.T) {
|
|||||||
t.Run("nestedUpdateOneToManyWithConnect", nestedUpdateOneToManyWithConnect)
|
t.Run("nestedUpdateOneToManyWithConnect", nestedUpdateOneToManyWithConnect)
|
||||||
t.Run("nestedUpdateOneToOneWithConnect", nestedUpdateOneToOneWithConnect)
|
t.Run("nestedUpdateOneToOneWithConnect", nestedUpdateOneToOneWithConnect)
|
||||||
t.Run("nestedUpdateOneToOneWithDisconnect", nestedUpdateOneToOneWithDisconnect)
|
t.Run("nestedUpdateOneToOneWithDisconnect", nestedUpdateOneToOneWithDisconnect)
|
||||||
|
//t.Run("nestedUpdateOneToOneWithDisconnectArray", nestedUpdateOneToOneWithDisconnectArray)
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,7 @@ type trval struct {
|
|||||||
query struct {
|
query struct {
|
||||||
limit string
|
limit string
|
||||||
fil *Exp
|
fil *Exp
|
||||||
|
filNU bool
|
||||||
cols map[string]struct{}
|
cols map[string]struct{}
|
||||||
disable struct {
|
disable struct {
|
||||||
funcs bool
|
funcs bool
|
||||||
@ -53,6 +54,7 @@ type trval struct {
|
|||||||
|
|
||||||
insert struct {
|
insert struct {
|
||||||
fil *Exp
|
fil *Exp
|
||||||
|
filNU bool
|
||||||
cols map[string]struct{}
|
cols map[string]struct{}
|
||||||
psmap map[string]string
|
psmap map[string]string
|
||||||
pslist []string
|
pslist []string
|
||||||
@ -60,14 +62,16 @@ type trval struct {
|
|||||||
|
|
||||||
update struct {
|
update struct {
|
||||||
fil *Exp
|
fil *Exp
|
||||||
|
filNU bool
|
||||||
cols map[string]struct{}
|
cols map[string]struct{}
|
||||||
psmap map[string]string
|
psmap map[string]string
|
||||||
pslist []string
|
pslist []string
|
||||||
}
|
}
|
||||||
|
|
||||||
delete struct {
|
delete struct {
|
||||||
fil *Exp
|
fil *Exp
|
||||||
cols map[string]struct{}
|
filNU bool
|
||||||
|
cols map[string]struct{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,21 +92,21 @@ func (trv *trval) allowedColumns(qt QType) map[string]struct{} {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (trv *trval) filter(qt QType) *Exp {
|
func (trv *trval) filter(qt QType) (*Exp, bool) {
|
||||||
switch qt {
|
switch qt {
|
||||||
case QTQuery:
|
case QTQuery:
|
||||||
return trv.query.fil
|
return trv.query.fil, trv.query.filNU
|
||||||
case QTInsert:
|
case QTInsert:
|
||||||
return trv.insert.fil
|
return trv.insert.fil, trv.insert.filNU
|
||||||
case QTUpdate:
|
case QTUpdate:
|
||||||
return trv.update.fil
|
return trv.update.fil, trv.update.filNU
|
||||||
case QTDelete:
|
case QTDelete:
|
||||||
return trv.delete.fil
|
return trv.delete.fil, trv.delete.filNU
|
||||||
case QTUpsert:
|
case QTUpsert:
|
||||||
return trv.insert.fil
|
return trv.insert.fil, trv.insert.filNU
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
func listToMap(list []string) map[string]struct{} {
|
func listToMap(list []string) map[string]struct{} {
|
||||||
|
@ -167,11 +167,6 @@ func parseSelectionSet(gql []byte) (*Operation, error) {
|
|||||||
return nil, fmt.Errorf("invalid '%s' found after closing '}'", p.current())
|
return nil, fmt.Errorf("invalid '%s' found after closing '}'", p.current())
|
||||||
}
|
}
|
||||||
|
|
||||||
// for i := p.pos; i < len(p.items); i++ {
|
|
||||||
// fmt.Printf("2>>>> %#v\n", p.items[i])
|
|
||||||
// }
|
|
||||||
//return nil, fmt.Errorf("unexpected token")
|
|
||||||
|
|
||||||
lexPool.Put(l)
|
lexPool.Put(l)
|
||||||
|
|
||||||
return op, err
|
return op, err
|
||||||
|
@ -51,6 +51,7 @@ type Select struct {
|
|||||||
Allowed map[string]struct{}
|
Allowed map[string]struct{}
|
||||||
PresetMap map[string]string
|
PresetMap map[string]string
|
||||||
PresetList []string
|
PresetList []string
|
||||||
|
SkipRender bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Column struct {
|
type Column struct {
|
||||||
@ -187,7 +188,7 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
|
|||||||
trv := &trval{}
|
trv := &trval{}
|
||||||
|
|
||||||
// query config
|
// query config
|
||||||
trv.query.fil, err = compileFilter(trc.Query.Filters)
|
trv.query.fil, trv.query.filNU, err = compileFilter(trc.Query.Filters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -198,7 +199,8 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
|
|||||||
trv.query.disable.funcs = trc.Query.DisableFunctions
|
trv.query.disable.funcs = trc.Query.DisableFunctions
|
||||||
|
|
||||||
// insert config
|
// insert config
|
||||||
if trv.insert.fil, err = compileFilter(trc.Insert.Filters); err != nil {
|
trv.insert.fil, trv.insert.filNU, err = compileFilter(trc.Insert.Filters)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trv.insert.cols = listToMap(trc.Insert.Columns)
|
trv.insert.cols = listToMap(trc.Insert.Columns)
|
||||||
@ -206,7 +208,8 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
|
|||||||
trv.insert.pslist = mapToList(trv.insert.psmap)
|
trv.insert.pslist = mapToList(trv.insert.psmap)
|
||||||
|
|
||||||
// update config
|
// update config
|
||||||
if trv.update.fil, err = compileFilter(trc.Update.Filters); err != nil {
|
trv.update.fil, trv.update.filNU, err = compileFilter(trc.Update.Filters)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trv.update.cols = listToMap(trc.Update.Columns)
|
trv.update.cols = listToMap(trc.Update.Columns)
|
||||||
@ -214,7 +217,8 @@ func (com *Compiler) AddRole(role, table string, trc TRConfig) error {
|
|||||||
trv.update.pslist = mapToList(trv.update.psmap)
|
trv.update.pslist = mapToList(trv.update.psmap)
|
||||||
|
|
||||||
// delete config
|
// delete config
|
||||||
if trv.delete.fil, err = compileFilter(trc.Delete.Filters); err != nil {
|
trv.delete.fil, trv.delete.filNU, err = compileFilter(trc.Delete.Filters)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
trv.delete.cols = listToMap(trc.Delete.Columns)
|
trv.delete.cols = listToMap(trc.Delete.Columns)
|
||||||
@ -334,7 +338,7 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
|||||||
s.FieldName = s.Name
|
s.FieldName = s.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
err := com.compileArgs(qc, s, field.Args)
|
err := com.compileArgs(qc, s, field.Args, role)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -388,9 +392,16 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
|||||||
|
|
||||||
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
|
||||||
|
|
||||||
if trv, ok := com.tr[role][sel.Name]; ok {
|
if trv, ok := com.tr[role][sel.Name]; ok {
|
||||||
fil = trv.filter(qc.Type)
|
fil, nu = trv.filter(qc.Type)
|
||||||
|
|
||||||
|
} else if role == "anon" {
|
||||||
|
// Tables not defined under the anon role will not be rendered
|
||||||
|
sel.SkipRender = true
|
||||||
|
return
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -399,6 +410,10 @@ func (com *Compiler) addFilters(qc *QCode, sel *Select, role string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nu && role == "anon" {
|
||||||
|
sel.SkipRender = true
|
||||||
|
}
|
||||||
|
|
||||||
switch fil.Op {
|
switch fil.Op {
|
||||||
case OpNop:
|
case OpNop:
|
||||||
case OpFalse:
|
case OpFalse:
|
||||||
@ -420,7 +435,7 @@ func (com *Compiler) addFilters(qc *QCode, sel *Select, role string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (com *Compiler) compileArgs(qc *QCode, sel *Select, args []Arg) error {
|
func (com *Compiler) compileArgs(qc *QCode, sel *Select, args []Arg, role string) error {
|
||||||
var err error
|
var err error
|
||||||
var ka bool
|
var ka bool
|
||||||
|
|
||||||
@ -435,7 +450,7 @@ func (com *Compiler) compileArgs(qc *QCode, sel *Select, args []Arg) error {
|
|||||||
err, ka = com.compileArgSearch(sel, arg)
|
err, ka = com.compileArgSearch(sel, arg)
|
||||||
|
|
||||||
case "where":
|
case "where":
|
||||||
err, ka = com.compileArgWhere(sel, arg)
|
err, ka = com.compileArgWhere(sel, arg, role)
|
||||||
|
|
||||||
case "orderby", "order_by", "order":
|
case "orderby", "order_by", "order":
|
||||||
err, ka = com.compileArgOrderBy(sel, arg)
|
err, ka = com.compileArgOrderBy(sel, arg)
|
||||||
@ -501,19 +516,20 @@ func (com *Compiler) setMutationType(qc *QCode, args []Arg) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (com *Compiler) compileArgObj(st *util.Stack, arg *Arg) (*Exp, error) {
|
func (com *Compiler) compileArgObj(st *util.Stack, arg *Arg) (*Exp, bool, error) {
|
||||||
if arg.Val.Type != NodeObj {
|
if arg.Val.Type != NodeObj {
|
||||||
return nil, fmt.Errorf("expecting an object")
|
return nil, false, fmt.Errorf("expecting an object")
|
||||||
}
|
}
|
||||||
|
|
||||||
return com.compileArgNode(st, arg.Val, true)
|
return com.compileArgNode(st, arg.Val, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (com *Compiler) compileArgNode(st *util.Stack, node *Node, usePool bool) (*Exp, error) {
|
func (com *Compiler) compileArgNode(st *util.Stack, node *Node, usePool bool) (*Exp, bool, error) {
|
||||||
var root *Exp
|
var root *Exp
|
||||||
|
var needsUser bool
|
||||||
|
|
||||||
if node == nil || len(node.Children) == 0 {
|
if node == nil || len(node.Children) == 0 {
|
||||||
return nil, errors.New("invalid argument value")
|
return nil, needsUser, errors.New("invalid argument value")
|
||||||
}
|
}
|
||||||
|
|
||||||
pushChild(st, nil, node)
|
pushChild(st, nil, node)
|
||||||
@ -526,7 +542,7 @@ func (com *Compiler) compileArgNode(st *util.Stack, node *Node, usePool bool) (*
|
|||||||
intf := st.Pop()
|
intf := st.Pop()
|
||||||
node, ok := intf.(*Node)
|
node, ok := intf.(*Node)
|
||||||
if !ok || node == nil {
|
if !ok || node == nil {
|
||||||
return nil, fmt.Errorf("16: unexpected value %v (%t)", intf, intf)
|
return nil, needsUser, fmt.Errorf("16: unexpected value %v (%t)", intf, intf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Objects inside a list
|
// Objects inside a list
|
||||||
@ -542,13 +558,17 @@ func (com *Compiler) compileArgNode(st *util.Stack, node *Node, usePool bool) (*
|
|||||||
|
|
||||||
ex, err := newExp(st, node, usePool)
|
ex, err := newExp(st, node, usePool)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, needsUser, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if ex == nil {
|
if ex == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ex.Type == ValVar && ex.Val == "user_id" {
|
||||||
|
needsUser = true
|
||||||
|
}
|
||||||
|
|
||||||
if node.exp == nil {
|
if node.exp == nil {
|
||||||
root = ex
|
root = ex
|
||||||
} else {
|
} else {
|
||||||
@ -571,7 +591,7 @@ func (com *Compiler) compileArgNode(st *util.Stack, node *Node, usePool bool) (*
|
|||||||
nodePool.Put(node)
|
nodePool.Put(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
return root, nil
|
return root, needsUser, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (com *Compiler) compileArgID(sel *Select, arg *Arg) (error, bool) {
|
func (com *Compiler) compileArgID(sel *Select, arg *Arg) (error, bool) {
|
||||||
@ -640,15 +660,19 @@ func (com *Compiler) compileArgSearch(sel *Select, arg *Arg) (error, bool) {
|
|||||||
return nil, true
|
return nil, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (com *Compiler) compileArgWhere(sel *Select, arg *Arg) (error, bool) {
|
func (com *Compiler) compileArgWhere(sel *Select, arg *Arg, role string) (error, bool) {
|
||||||
st := util.NewStack()
|
st := util.NewStack()
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
ex, err := com.compileArgObj(st, arg)
|
ex, nu, err := com.compileArgObj(st, arg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err, false
|
return err, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if nu && role == "anon" {
|
||||||
|
sel.SkipRender = true
|
||||||
|
}
|
||||||
|
|
||||||
if sel.Where != nil {
|
if sel.Where != nil {
|
||||||
ow := sel.Where
|
ow := sel.Where
|
||||||
|
|
||||||
@ -976,27 +1000,32 @@ func pushChild(st *util.Stack, exp *Exp, node *Node) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileFilter(filter []string) (*Exp, error) {
|
func compileFilter(filter []string) (*Exp, bool, error) {
|
||||||
var fl *Exp
|
var fl *Exp
|
||||||
|
var needsUser bool
|
||||||
|
|
||||||
com := &Compiler{}
|
com := &Compiler{}
|
||||||
st := util.NewStack()
|
st := util.NewStack()
|
||||||
|
|
||||||
if len(filter) == 0 {
|
if len(filter) == 0 {
|
||||||
return &Exp{Op: OpNop, doFree: false}, nil
|
return &Exp{Op: OpNop, doFree: false}, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range filter {
|
for i := range filter {
|
||||||
if filter[i] == "false" {
|
if filter[i] == "false" {
|
||||||
return &Exp{Op: OpFalse, doFree: false}, nil
|
return &Exp{Op: OpFalse, doFree: false}, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
node, err := ParseArgValue(filter[i])
|
node, err := ParseArgValue(filter[i])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
f, err := com.compileArgNode(st, node, false)
|
f, nu, err := com.compileArgNode(st, node, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, false, err
|
||||||
|
}
|
||||||
|
if nu {
|
||||||
|
needsUser = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Invalid table names in nested where causes fail silently
|
// TODO: Invalid table names in nested where causes fail silently
|
||||||
@ -1010,7 +1039,7 @@ func compileFilter(filter []string) (*Exp, error) {
|
|||||||
fl = &Exp{Op: OpAnd, Children: []*Exp{fl, f}, doFree: false}
|
fl = &Exp{Op: OpAnd, Children: []*Exp{fl, f}, doFree: false}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fl, nil
|
return fl, needsUser, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildPath(a []string) string {
|
func buildPath(a []string) string {
|
||||||
|
@ -35,8 +35,6 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
|
|||||||
|
|
||||||
fields := jsn.Get(vars, [][]byte{[]byte(tag)})
|
fields := jsn.Get(vars, [][]byte{[]byte(tag)})
|
||||||
|
|
||||||
fmt.Println(">>", tag, string(vars))
|
|
||||||
|
|
||||||
if len(fields) == 0 {
|
if len(fields) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,15 @@ func cmdServ(cmd *cobra.Command, args []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if conf != nil {
|
if conf != nil {
|
||||||
if db, err = initDBPool(conf); err != nil {
|
db, err = initDBPool(conf)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
initCompiler()
|
||||||
|
initAllowList(confPath)
|
||||||
|
initPreparedList()
|
||||||
|
} else {
|
||||||
fatalInProd(err, "failed to connect to database")
|
fatalInProd(err, "failed to connect to database")
|
||||||
}
|
}
|
||||||
|
|
||||||
initCompiler()
|
|
||||||
initAllowList(confPath)
|
|
||||||
initPreparedList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initWatcher(confPath)
|
initWatcher(confPath)
|
||||||
|
Reference in New Issue
Block a user