Compare commits

...

8 Commits

16 changed files with 142 additions and 56 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -335,10 +335,11 @@ func (c *compilerContext) renderUnionStmt(w io.Writer, item renitem) error {
}
io.WriteString(w, ` RETURNING `)
quoted(w, item.ti.Name)
io.WriteString(w, `.*), `)
io.WriteString(w, `.*)`)
}
if connect && disconnect {
io.WriteString(w, `, `)
quoted(w, item.ti.Name)
io.WriteString(w, ` AS (`)
io.WriteString(w, `SELECT * FROM `)

View File

@ -932,7 +932,6 @@ func (c *compilerContext) renderNestedWhere(ex *qcode.Exp, ti *DBTableInfo) erro
return err
}
//fmt.Println(">", ex)
io.WriteString(c.w, `)`)
}

View File

@ -209,20 +209,20 @@ func oneToManyReverse(t *testing.T) {
}
func oneToManyArray(t *testing.T) {
gql := `query {
gql := `
query {
product {
name
price
tags {
id
name
}
}
tags {
name
product {
name
price
tags {
id
name
}
}
tags {
name
product {
name
}
}
}
}`

View File

@ -5,7 +5,6 @@ import (
"strings"
"github.com/gobuffalo/flect"
"github.com/jackc/pgx/v4/pgxpool"
)
type DBSchema struct {
@ -51,8 +50,7 @@ type DBRel struct {
}
}
func NewDBSchema(db *pgxpool.Pool,
info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
func NewDBSchema(info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
schema := &DBSchema{
t: make(map[string]*DBTableInfo),
@ -322,8 +320,21 @@ func (s *DBSchema) SetRel(child, parent string, rel *DBRel) error {
func (s *DBSchema) GetRel(child, parent string) (*DBRel, error) {
rel, ok := s.rm[child][parent]
if !ok {
return nil, fmt.Errorf("unknown relationship '%s' -> '%s'",
child, parent)
// No relationship found so this time fetch the table info
// and try again in case child or parent was an alias
ct, err := s.GetTable(child)
if err != nil {
return nil, err
}
pt, err := s.GetTable(parent)
if err != nil {
return nil, err
}
rel, ok = s.rm[ct.Name][pt.Name]
if !ok {
return nil, fmt.Errorf("unknown relationship '%s' -> '%s'",
child, parent)
}
}
return rel, nil
}

View File

@ -31,7 +31,7 @@ type item struct {
_type itemType // The type of this item.
pos Pos // The starting position, in bytes, of this item in the input string.
end Pos // The ending position, in bytes, of this item in the input string.
line uint16 // The line number at the start of this item.
line int16 // The line number at the start of this item.
}
// itemType identifies the type of lex items.
@ -87,7 +87,7 @@ type lexer struct {
width Pos // width of last rune read from input
items []item // array of scanned items
itemsA [50]item
line uint16 // 1+number of newlines seen
line int16 // 1+number of newlines seen
err error
}
@ -137,7 +137,7 @@ func (l *lexer) emit(t itemType) {
l.items = append(l.items, item{t, l.start, l.pos, l.line})
// Some items contain text internally. If so, count their newlines.
switch t {
case itemName:
case itemStringVal:
for i := l.start; i < l.pos; i++ {
if l.input[i] == '\n' {
l.line++
@ -155,11 +155,6 @@ func (l *lexer) emitL(t itemType) {
// ignore skips over the pending input before this point.
func (l *lexer) ignore() {
for i := l.start; i < l.pos; i++ {
if l.input[i] == '\n' {
l.line++
}
}
l.start = l.pos
}
@ -436,7 +431,7 @@ func lowercase(b []byte, s Pos, e Pos) {
}
}
func (i *item) String() string {
func (i item) String() string {
var v string
switch i._type {

View File

@ -156,12 +156,24 @@ func parseSelectionSet(gql []byte) (*Operation, error) {
return nil, err
}
lexPool.Put(l)
if err != nil {
return nil, err
if p.peek(itemObjClose) {
p.ignore()
} else {
return nil, fmt.Errorf("operation missing closing '}'")
}
if !p.peek(itemEOF) {
p.ignore()
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)
return op, err
}
@ -184,11 +196,16 @@ func (p *Parser) ignore() {
p.pos = n
}
func (p *Parser) current() string {
item := p.items[p.pos]
return b2s(p.input[item.pos:item.end])
}
func (p *Parser) peek(types ...itemType) bool {
n := p.pos + 1
if p.items[n]._type == itemEOF {
return false
}
// if p.items[n]._type == itemEOF {
// return false
// }
if n >= len(p.items) {
return false
}
@ -292,8 +309,9 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
if st.Len() == 0 {
break
} else {
continue
}
continue
}
if !p.peek(itemName) {
@ -306,6 +324,8 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
f.Args = f.argsA[:0]
f.Children = f.childrenA[:0]
// Parse the inside of the the fields () parentheses
// in short parse the args like id, where, etc
if err := p.parseField(f); err != nil {
return nil, err
}
@ -318,6 +338,8 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
f.ParentID = -1
}
// The first opening curley brackets after this
// comes the columns or child fields
if p.peek(itemObjOpen) {
p.ignore()
st.Push(f.ID)

View File

@ -17,7 +17,7 @@ func TestCompile1(t *testing.T) {
}
_, err = qc.Compile([]byte(`
{ product(id: 15) {
query { product(id: 15) {
id
name
} }`), "user")
@ -100,6 +100,35 @@ func TestEmptyCompile(t *testing.T) {
}
}
func TestInvalidPostfixCompile(t *testing.T) {
gql := `mutation
updateThread {
thread(update: $data, where: { slug: { eq: $slug } }) {
slug
title
published
createdAt : created_at
totalVotes : cached_votes_total
totalPosts : cached_posts_total
vote : thread_vote(where: { user_id: { eq: $user_id } }) {
id
}
topics {
slug
name
}
}
}
}`
qcompile, _ := NewCompiler(Config{})
_, err := qcompile.Compile([]byte(gql), "anon")
if err == nil {
t.Fatal(errors.New("expecting an error"))
}
}
var gql = []byte(`
products(
# returns only 30 items

View File

@ -34,6 +34,7 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
}
fields := jsn.Get(vars, [][]byte{[]byte(tag)})
if len(fields) == 0 {
return 0, nil
}
@ -42,7 +43,7 @@ func argMap(ctx context.Context, vars []byte) func(w io.Writer, tag string) (int
fields[0].Value = v[1 : len(v)-1]
}
return w.Write(fields[0].Value)
return w.Write(escQuote(fields[0].Value))
}
}
@ -89,7 +90,7 @@ func argList(ctx *coreContext, args [][]byte) ([]interface{}, error) {
if v, ok := fields[string(av)]; ok {
switch v[0] {
case '[', '{':
vars[i] = v
vars[i] = escQuote(v)
default:
var val interface{}
if err := json.Unmarshal(v, &val); err != nil {
@ -106,3 +107,31 @@ func argList(ctx *coreContext, args [][]byte) ([]interface{}, error) {
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)
})
ifNotExists(path.Join(appMigrationsPath, "100_init.sql"), func(p string) error {
if v, err := tmpl.get("100_init.sql"); err == nil {
ifNotExists(path.Join(appMigrationsPath, "0_init.sql"), func(p string) error {
if v, err := tmpl.get("0_init.sql"); err == nil {
return ioutil.WriteFile(p, v, 0644)
} else {
return err

View File

@ -25,7 +25,7 @@ func initCompilers(c *config) (*qcode.Compiler, *psql.Compiler, error) {
return nil, nil, err
}
schema, err = psql.NewDBSchema(db, di, c.getAliasMap())
schema, err = psql.NewDBSchema(di, c.getAliasMap())
if err != nil {
return nil, nil, err
}