Compare commits

...

3 Commits

13 changed files with 98 additions and 43 deletions

View File

@ -64,7 +64,7 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error {
state = expectKeyClose state = expectKeyClose
s = i s = i
case state == expectKeyClose && b[i] == '"': case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
state = expectColon state = expectColon
k = b[(s + 1):i] k = b[(s + 1):i]
@ -74,7 +74,7 @@ func Filter(w *bytes.Buffer, b []byte, keys []string) error {
case state == expectValue && b[i] == '"': case state == expectValue && b[i] == '"':
state = expectString state = expectString
case state == expectString && b[i] == '"': case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
e = i e = i
case state == expectValue && b[i] == '[': case state == expectValue && b[i] == '[':

View File

@ -66,7 +66,7 @@ func Get(b []byte, keys [][]byte) []Field {
state = expectKeyClose state = expectKeyClose
s = i s = i
case state == expectKeyClose && b[i] == '"': case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
state = expectColon state = expectColon
k = b[(s + 1):i] k = b[(s + 1):i]
@ -77,7 +77,7 @@ func Get(b []byte, keys [][]byte) []Field {
state = expectString state = expectString
s = i s = i
case state == expectString && b[i] == '"': case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
e = i e = i
case state == expectValue && b[i] == '[': case state == expectValue && b[i] == '[':

View File

@ -13,12 +13,12 @@ var (
"users": [ "users": [
{ {
"id": 1, "id": 1,
"full_name": "Sidney Stroman", "full_name": "'Sidney Stroman'",
"email": "user0@demo.com", "email": "user0@demo.com",
"__twitter_id": "2048666903444506956", "__twitter_id": "2048666903444506956",
"embed": { "embed": {
"id": 8, "id": 8,
"full_name": "Caroll Orn Sr.", "full_name": "Caroll Orn Sr's",
"email": "joannarau@hegmann.io", "email": "joannarau@hegmann.io",
"__twitter_id": "ABC123" "__twitter_id": "ABC123"
"more": [{ "more": [{
@ -37,7 +37,7 @@ var (
"id": 3, "id": 3,
"full_name": "Kenna Cassin", "full_name": "Kenna Cassin",
"email": "user2@demo.com", "email": "user2@demo.com",
"__twitter_id": { "name": "hello", "address": { "work": "1 infinity loop" } } "__twitter_id": { "name": "\"hellos\"", "address": { "work": "1 infinity loop" } }
}, },
{ {
"id": 4, "id": 4,
@ -171,7 +171,7 @@ func TestGet(t *testing.T) {
{[]byte("__twitter_id"), {[]byte("__twitter_id"),
[]byte(`[{ "name": "hello" }, { "name": "world"}]`)}, []byte(`[{ "name": "hello" }, { "name": "world"}]`)},
{[]byte("__twitter_id"), {[]byte("__twitter_id"),
[]byte(`{ "name": "hello", "address": { "work": "1 infinity loop" } }`), []byte(`{ "name": "\"hellos\"", "address": { "work": "1 infinity loop" } }`),
}, },
{[]byte("__twitter_id"), []byte(`1234567890`)}, {[]byte("__twitter_id"), []byte(`1234567890`)},
{[]byte("__twitter_id"), []byte(`1.23E`)}, {[]byte("__twitter_id"), []byte(`1.23E`)},

View File

@ -47,7 +47,7 @@ func Keys(b []byte) [][]byte {
state = expectKeyClose state = expectKeyClose
s = i s = i
case state == expectKeyClose && b[i] == '"': case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
state = expectColon state = expectColon
k = b[(s + 1):i] k = b[(s + 1):i]
@ -58,7 +58,7 @@ func Keys(b []byte) [][]byte {
state = expectString state = expectString
s = i s = i
case state == expectString && b[i] == '"': case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
e = i e = i
case state == expectValue && b[i] == '{': case state == expectValue && b[i] == '{':

View File

@ -52,7 +52,7 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
state = expectKeyClose state = expectKeyClose
s = i s = i
case state == expectKeyClose && b[i] == '"': case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
state = expectColon state = expectColon
if _, err := h.Write(b[(s + 1):i]); err != nil { if _, err := h.Write(b[(s + 1):i]); err != nil {
return err return err
@ -66,7 +66,7 @@ func Replace(w *bytes.Buffer, b []byte, from, to []Field) error {
state = expectString state = expectString
s = i s = i
case state == expectString && b[i] == '"': case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
e = i e = i
case state == expectValue && b[i] == '[': case state == expectValue && b[i] == '[':

View File

@ -27,7 +27,7 @@ func Strip(b []byte, path [][]byte) []byte {
state = expectKeyClose state = expectKeyClose
s = i s = i
case state == expectKeyClose && b[i] == '"': case state == expectKeyClose && (b[i-1] != '\\' && b[i] == '"'):
state = expectColon state = expectColon
if pi == len(path) { if pi == len(path) {
pi = 0 pi = 0
@ -44,7 +44,7 @@ func Strip(b []byte, path [][]byte) []byte {
state = expectString state = expectString
s = i s = i
case state == expectString && b[i] == '"': case state == expectString && (b[i-1] != '\\' && b[i] == '"'):
e = i e = i
case state == expectValue && b[i] == '[': case state == expectValue && b[i] == '[':

View File

@ -814,11 +814,15 @@ func (c *compilerContext) renderRelationshipByName(table, parent string, id int3
} }
func (c *compilerContext) renderWhere(sel *qcode.Select, ti *DBTableInfo) error { func (c *compilerContext) renderWhere(sel *qcode.Select, ti *DBTableInfo) error {
st := util.NewStack()
if sel.Where != nil { if sel.Where != nil {
st.Push(sel.Where) return c.renderExp(sel.Where, ti, false)
} }
return nil
}
func (c *compilerContext) renderExp(ex *qcode.Exp, ti *DBTableInfo, skipNested bool) error {
st := util.NewStack()
st.Push(ex)
for { for {
if st.Len() == 0 { if st.Len() == 0 {
@ -873,16 +877,16 @@ func (c *compilerContext) renderWhere(sel *qcode.Select, ti *DBTableInfo) error
qcode.FreeExp(val) qcode.FreeExp(val)
default: default:
if len(val.NestedCols) != 0 { if !skipNested && len(val.NestedCols) != 0 {
io.WriteString(c.w, `EXISTS `) io.WriteString(c.w, `EXISTS `)
if err := c.renderNestedWhere(val, sel, ti); err != nil { if err := c.renderNestedWhere(val, ti); err != nil {
return err return err
} }
} else { } else {
//fmt.Fprintf(w, `(("%s"."%s") `, c.sel.Name, val.Col) //fmt.Fprintf(w, `(("%s"."%s") `, c.sel.Name, val.Col)
if err := c.renderOp(val, sel, ti); err != nil { if err := c.renderOp(val, ti); err != nil {
return err return err
} }
qcode.FreeExp(val) qcode.FreeExp(val)
@ -898,7 +902,7 @@ func (c *compilerContext) renderWhere(sel *qcode.Select, ti *DBTableInfo) error
return nil return nil
} }
func (c *compilerContext) renderNestedWhere(ex *qcode.Exp, sel *qcode.Select, ti *DBTableInfo) error { func (c *compilerContext) renderNestedWhere(ex *qcode.Exp, ti *DBTableInfo) error {
for i := 0; i < len(ex.NestedCols)-1; i++ { for i := 0; i < len(ex.NestedCols)-1; i++ {
cti, err := c.schema.GetTable(ex.NestedCols[i]) cti, err := c.schema.GetTable(ex.NestedCols[i])
if err != nil { if err != nil {
@ -922,6 +926,15 @@ func (c *compilerContext) renderNestedWhere(ex *qcode.Exp, sel *qcode.Select, ti
return err return err
} }
io.WriteString(c.w, ` AND (`)
if err := c.renderExp(ex, cti, true); err != nil {
return err
}
//fmt.Println(">", ex)
io.WriteString(c.w, `)`)
} }
for i := 0; i < len(ex.NestedCols)-1; i++ { for i := 0; i < len(ex.NestedCols)-1; i++ {
@ -931,7 +944,7 @@ func (c *compilerContext) renderNestedWhere(ex *qcode.Exp, sel *qcode.Select, ti
return nil return nil
} }
func (c *compilerContext) renderOp(ex *qcode.Exp, sel *qcode.Select, ti *DBTableInfo) error { func (c *compilerContext) renderOp(ex *qcode.Exp, ti *DBTableInfo) error {
var col *DBColumn var col *DBColumn
var ok bool var ok bool
@ -1029,10 +1042,9 @@ func (c *compilerContext) renderOp(ex *qcode.Exp, sel *qcode.Select, ti *DBTable
if ex.Type == qcode.ValList { if ex.Type == qcode.ValList {
c.renderList(ex) c.renderList(ex)
} else { } else if col == nil {
if col == nil {
return errors.New("no column found for expression value") return errors.New("no column found for expression value")
} } else {
c.renderVal(ex, c.vars, col) c.renderVal(ex, c.vars, col)
} }

View File

@ -418,7 +418,7 @@ func withWhereOnRelations(t *testing.T) {
} }
}` }`
sql := `SELECT json_object_agg('users', json_0) FROM (SELECT coalesce(json_agg("json_0"), '[]') AS "json_0" FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."email" AS "email") AS "json_row_0")) AS "json_0" FROM (SELECT "users"."id", "users"."email" FROM "users" WHERE (NOT EXISTS (SELECT 1 FROM products WHERE (("products"."user_id") = ("users"."id")))) LIMIT ('20') :: integer) AS "users_0" LIMIT ('20') :: integer) AS "json_agg_0") AS "sel_0"` sql := `SELECT json_object_agg('users', json_0) FROM (SELECT coalesce(json_agg("json_0"), '[]') AS "json_0" FROM (SELECT row_to_json((SELECT "json_row_0" FROM (SELECT "users_0"."id" AS "id", "users_0"."email" AS "email") AS "json_row_0")) AS "json_0" 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)))) LIMIT ('20') :: integer) AS "users_0" LIMIT ('20') :: integer) AS "json_agg_0") AS "sel_0"`
resSQL, err := compileGQLToPSQL(gql, nil, "user") resSQL, err := compileGQLToPSQL(gql, nil, "user")
if err != nil { if err != nil {

View File

@ -80,7 +80,7 @@ func (trv *trval) allowedColumns(qt QType) map[string]struct{} {
case QTUpdate: case QTUpdate:
return trv.update.cols return trv.update.cols
case QTDelete: case QTDelete:
return trv.insert.cols return trv.delete.cols
case QTUpsert: case QTUpsert:
return trv.insert.cols return trv.insert.cols
} }

View File

@ -940,10 +940,13 @@ func setWhereColName(ex *Exp, node *Node) {
list = append([]string{k}, list...) list = append([]string{k}, list...)
} }
} }
if len(list) == 1 { listlen := len(list)
if listlen == 1 {
ex.Col = list[0] ex.Col = list[0]
} else if len(list) > 1 { } else if listlen > 1 {
ex.NestedCols = list ex.Col = list[listlen-1]
ex.NestedCols = list[:listlen]
} }
} }
@ -996,6 +999,11 @@ func compileFilter(filter []string) (*Exp, error) {
return nil, err return nil, err
} }
// TODO: Invalid table names in nested where causes fail silently
// returning a nil 'f' this needs to be fixed
// TODO: Invalid where clauses such as missing op (eg. eq) also fail silently
if fl == nil { if fl == nil {
fl = f fl = f
} else { } else {

View File

@ -33,7 +33,12 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
return 0, errors.New("query requires variable $user_role") return 0, errors.New("query requires variable $user_role")
} }
fmt.Println("1>", tag)
fmt.Println("2>", string(vars))
fields := jsn.Get(vars, [][]byte{[]byte(tag)}) fields := jsn.Get(vars, [][]byte{[]byte(tag)})
fmt.Println("2.1>", fields)
if len(fields) == 0 { if len(fields) == 0 {
return 0, nil return 0, nil
} }
@ -42,7 +47,9 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
fields[0].Value = v[1 : len(v)-1] fields[0].Value = v[1 : len(v)-1]
} }
return w.Write(fields[0].Value) fmt.Println("3>", string(fields[0].Value))
return w.Write(escQuote(fields[0].Value))
} }
} }
@ -89,7 +96,7 @@ func argList(ctx *coreContext, args [][]byte) ([]interface{}, error) {
if v, ok := fields[string(av)]; ok { if v, ok := fields[string(av)]; ok {
switch v[0] { switch v[0] {
case '[', '{': case '[', '{':
vars[i] = v vars[i] = escQuote(v)
default: default:
var val interface{} var val interface{}
if err := json.Unmarshal(v, &val); err != nil { if err := json.Unmarshal(v, &val); err != nil {
@ -106,3 +113,31 @@ func argList(ctx *coreContext, args [][]byte) ([]interface{}, error) {
return vars, nil return vars, nil
} }
func escQuote(b []byte) []byte {
f := false
for i := range b {
if b[i] == '\'' {
f = true
break
}
}
if !f {
return b
}
buf := &bytes.Buffer{}
s := 0
for i := range b {
if b[i] == '\'' {
buf.Write(b[s:i])
buf.WriteString(`''`)
s = i + 1
}
}
l := len(b)
if s < (l - 1) {
buf.Write(b[s:l])
}
return buf.Bytes()
}

View File

@ -90,8 +90,8 @@ func cmdNew(cmd *cobra.Command, args []string) {
return os.Mkdir(p, os.ModePerm) return os.Mkdir(p, os.ModePerm)
}) })
ifNotExists(path.Join(appMigrationsPath, "100_init.sql"), func(p string) error { ifNotExists(path.Join(appMigrationsPath, "0_init.sql"), func(p string) error {
if v, err := tmpl.get("100_init.sql"); err == nil { if v, err := tmpl.get("0_init.sql"); err == nil {
return ioutil.WriteFile(p, v, 0644) return ioutil.WriteFile(p, v, 0644)
} else { } else {
return err return err

View File

@ -80,26 +80,26 @@ func addRole(qc *qcode.Compiler, r configRole, t configRoleTable) error {
Presets: t.Insert.Presets, Presets: t.Insert.Presets,
} }
if t.Query.Block { if t.Insert.Block {
insert.Filters = blockFilter insert.Filters = blockFilter
} }
update := qcode.UpdateConfig{ update := qcode.UpdateConfig{
Filters: t.Insert.Filters, Filters: t.Update.Filters,
Columns: t.Insert.Columns, Columns: t.Update.Columns,
Presets: t.Insert.Presets, Presets: t.Update.Presets,
} }
if t.Query.Block { if t.Update.Block {
update.Filters = blockFilter update.Filters = blockFilter
} }
delete := qcode.DeleteConfig{ delete := qcode.DeleteConfig{
Filters: t.Insert.Filters, Filters: t.Delete.Filters,
Columns: t.Insert.Columns, Columns: t.Delete.Columns,
} }
if t.Query.Block { if t.Delete.Block {
delete.Filters = blockFilter delete.Filters = blockFilter
} }