Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
d4dca86267 | |||
3f5727c22b | |||
7c02226016 |
@ -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] == '[':
|
||||||
|
@ -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] == '[':
|
||||||
|
@ -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`)},
|
||||||
|
@ -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] == '{':
|
||||||
|
@ -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] == '[':
|
||||||
|
@ -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] == '[':
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
39
serv/args.go
39
serv/args.go
@ -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()
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user