Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
3eb5b83070 | |||
e3c94d17d1 | |||
7240b27214 | |||
f37d867e32 | |||
5e75cc7b83 | |||
76340ab008 |
@ -335,10 +335,11 @@ func (c *compilerContext) renderUnionStmt(w io.Writer, item renitem) error {
|
|||||||
}
|
}
|
||||||
io.WriteString(w, ` RETURNING `)
|
io.WriteString(w, ` RETURNING `)
|
||||||
quoted(w, item.ti.Name)
|
quoted(w, item.ti.Name)
|
||||||
io.WriteString(w, `.*), `)
|
io.WriteString(w, `.*)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if connect && disconnect {
|
if connect && disconnect {
|
||||||
|
io.WriteString(w, `, `)
|
||||||
quoted(w, item.ti.Name)
|
quoted(w, item.ti.Name)
|
||||||
io.WriteString(w, ` AS (`)
|
io.WriteString(w, ` AS (`)
|
||||||
io.WriteString(w, `SELECT * FROM `)
|
io.WriteString(w, `SELECT * FROM `)
|
||||||
|
@ -932,7 +932,6 @@ func (c *compilerContext) renderNestedWhere(ex *qcode.Exp, ti *DBTableInfo) erro
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
//fmt.Println(">", ex)
|
|
||||||
io.WriteString(c.w, `)`)
|
io.WriteString(c.w, `)`)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -209,20 +209,20 @@ func oneToManyReverse(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func oneToManyArray(t *testing.T) {
|
func oneToManyArray(t *testing.T) {
|
||||||
gql := `query {
|
gql := `
|
||||||
|
query {
|
||||||
|
product {
|
||||||
|
name
|
||||||
|
price
|
||||||
|
tags {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tags {
|
||||||
|
name
|
||||||
product {
|
product {
|
||||||
name
|
name
|
||||||
price
|
|
||||||
tags {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tags {
|
|
||||||
name
|
|
||||||
product {
|
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gobuffalo/flect"
|
"github.com/gobuffalo/flect"
|
||||||
"github.com/jackc/pgx/v4/pgxpool"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DBSchema struct {
|
type DBSchema struct {
|
||||||
@ -51,8 +50,7 @@ type DBRel struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewDBSchema(db *pgxpool.Pool,
|
func NewDBSchema(info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
|
||||||
info *DBInfo, aliases map[string][]string) (*DBSchema, error) {
|
|
||||||
|
|
||||||
schema := &DBSchema{
|
schema := &DBSchema{
|
||||||
t: make(map[string]*DBTableInfo),
|
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) {
|
func (s *DBSchema) GetRel(child, parent string) (*DBRel, error) {
|
||||||
rel, ok := s.rm[child][parent]
|
rel, ok := s.rm[child][parent]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("unknown relationship '%s' -> '%s'",
|
// No relationship found so this time fetch the table info
|
||||||
child, parent)
|
// 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
|
return rel, nil
|
||||||
}
|
}
|
||||||
|
13
qcode/lex.go
13
qcode/lex.go
@ -31,7 +31,7 @@ type item struct {
|
|||||||
_type itemType // The type of this item.
|
_type itemType // The type of this item.
|
||||||
pos Pos // The starting position, in bytes, of this item in the input string.
|
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.
|
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.
|
// itemType identifies the type of lex items.
|
||||||
@ -87,7 +87,7 @@ type lexer struct {
|
|||||||
width Pos // width of last rune read from input
|
width Pos // width of last rune read from input
|
||||||
items []item // array of scanned items
|
items []item // array of scanned items
|
||||||
itemsA [50]item
|
itemsA [50]item
|
||||||
line uint16 // 1+number of newlines seen
|
line int16 // 1+number of newlines seen
|
||||||
err error
|
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})
|
l.items = append(l.items, item{t, l.start, l.pos, l.line})
|
||||||
// Some items contain text internally. If so, count their newlines.
|
// Some items contain text internally. If so, count their newlines.
|
||||||
switch t {
|
switch t {
|
||||||
case itemName:
|
case itemStringVal:
|
||||||
for i := l.start; i < l.pos; i++ {
|
for i := l.start; i < l.pos; i++ {
|
||||||
if l.input[i] == '\n' {
|
if l.input[i] == '\n' {
|
||||||
l.line++
|
l.line++
|
||||||
@ -155,11 +155,6 @@ func (l *lexer) emitL(t itemType) {
|
|||||||
|
|
||||||
// ignore skips over the pending input before this point.
|
// ignore skips over the pending input before this point.
|
||||||
func (l *lexer) ignore() {
|
func (l *lexer) ignore() {
|
||||||
for i := l.start; i < l.pos; i++ {
|
|
||||||
if l.input[i] == '\n' {
|
|
||||||
l.line++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
l.start = l.pos
|
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
|
var v string
|
||||||
|
|
||||||
switch i._type {
|
switch i._type {
|
||||||
|
@ -156,12 +156,24 @@ func parseSelectionSet(gql []byte) (*Operation, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
lexPool.Put(l)
|
if p.peek(itemObjClose) {
|
||||||
|
p.ignore()
|
||||||
if err != nil {
|
} else {
|
||||||
return nil, err
|
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
|
return op, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,11 +196,16 @@ func (p *Parser) ignore() {
|
|||||||
p.pos = n
|
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 {
|
func (p *Parser) peek(types ...itemType) bool {
|
||||||
n := p.pos + 1
|
n := p.pos + 1
|
||||||
if p.items[n]._type == itemEOF {
|
// if p.items[n]._type == itemEOF {
|
||||||
return false
|
// return false
|
||||||
}
|
// }
|
||||||
if n >= len(p.items) {
|
if n >= len(p.items) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -292,8 +309,9 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
|
|||||||
|
|
||||||
if st.Len() == 0 {
|
if st.Len() == 0 {
|
||||||
break
|
break
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !p.peek(itemName) {
|
if !p.peek(itemName) {
|
||||||
@ -306,6 +324,8 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
|
|||||||
f.Args = f.argsA[:0]
|
f.Args = f.argsA[:0]
|
||||||
f.Children = f.childrenA[: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 {
|
if err := p.parseField(f); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -318,6 +338,8 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
|
|||||||
f.ParentID = -1
|
f.ParentID = -1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The first opening curley brackets after this
|
||||||
|
// comes the columns or child fields
|
||||||
if p.peek(itemObjOpen) {
|
if p.peek(itemObjOpen) {
|
||||||
p.ignore()
|
p.ignore()
|
||||||
st.Push(f.ID)
|
st.Push(f.ID)
|
||||||
|
@ -17,7 +17,7 @@ func TestCompile1(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_, err = qc.Compile([]byte(`
|
_, err = qc.Compile([]byte(`
|
||||||
{ product(id: 15) {
|
query { product(id: 15) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
} }`), "user")
|
} }`), "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(`
|
var gql = []byte(`
|
||||||
products(
|
products(
|
||||||
# returns only 30 items
|
# returns only 30 items
|
||||||
|
@ -33,11 +33,7 @@ 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
|
||||||
@ -47,8 +43,6 @@ 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]
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("3>", string(fields[0].Value))
|
|
||||||
|
|
||||||
return w.Write(escQuote(fields[0].Value))
|
return w.Write(escQuote(fields[0].Value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ func initCompilers(c *config) (*qcode.Compiler, *psql.Compiler, error) {
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
schema, err = psql.NewDBSchema(db, di, c.getAliasMap())
|
schema, err = psql.NewDBSchema(di, c.getAliasMap())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user