Added support for multi-root queries
This commit is contained in:
@ -148,24 +148,13 @@ func parseSelectionSet(gql []byte) (*Operation, error) {
|
||||
|
||||
if p.peek(itemObjOpen) {
|
||||
p.ignore()
|
||||
}
|
||||
|
||||
if p.peek(itemName) {
|
||||
op = opPool.Get().(*Operation)
|
||||
op.Reset()
|
||||
|
||||
op.Type = opQuery
|
||||
op.Name = ""
|
||||
op.Fields = op.fieldsA[:0]
|
||||
op.Args = op.argsA[:0]
|
||||
op.Fields, err = p.parseFields(op.Fields)
|
||||
|
||||
op, err = p.parseQueryOp()
|
||||
} else {
|
||||
op, err = p.parseOp()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lexPool.Put(l)
|
||||
@ -259,6 +248,37 @@ func (p *Parser) parseOp() (*Operation, error) {
|
||||
|
||||
if p.peek(itemObjOpen) {
|
||||
p.ignore()
|
||||
|
||||
for n := 0; n < 10; n++ {
|
||||
if p.peek(itemName) == false {
|
||||
break
|
||||
}
|
||||
|
||||
op.Fields, err = p.parseFields(op.Fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return op, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseQueryOp() (*Operation, error) {
|
||||
op := opPool.Get().(*Operation)
|
||||
op.Reset()
|
||||
|
||||
op.Type = opQuery
|
||||
op.Fields = op.fieldsA[:0]
|
||||
op.Args = op.argsA[:0]
|
||||
|
||||
var err error
|
||||
|
||||
for n := 0; n < 10; n++ {
|
||||
if p.peek(itemName) == false {
|
||||
break
|
||||
}
|
||||
|
||||
op.Fields, err = p.parseFields(op.Fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -300,16 +320,12 @@ func (p *Parser) parseFields(fields []Field) ([]Field, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if f.ID != 0 {
|
||||
intf := st.Peek()
|
||||
pid, ok := intf.(int32)
|
||||
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("14: unexpected value %v (%t)", intf, intf)
|
||||
}
|
||||
|
||||
intf := st.Peek()
|
||||
if pid, ok := intf.(int32); ok {
|
||||
f.ParentID = pid
|
||||
fields[pid].Children = append(fields[pid].Children, f.ID)
|
||||
} else {
|
||||
f.ParentID = -1
|
||||
}
|
||||
|
||||
if p.peek(itemObjOpen) {
|
||||
|
@ -14,10 +14,10 @@ func TestCompile1(t *testing.T) {
|
||||
})
|
||||
|
||||
_, err := qc.Compile([]byte(`
|
||||
product(id: 15) {
|
||||
{ product(id: 15) {
|
||||
id
|
||||
name
|
||||
}`), "user")
|
||||
} }`), "user")
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -30,6 +30,8 @@ type QCode struct {
|
||||
Type QType
|
||||
ActionVar string
|
||||
Selects []Select
|
||||
Roots []int32
|
||||
rootsA [5]int32
|
||||
}
|
||||
|
||||
type Select struct {
|
||||
@ -233,6 +235,7 @@ func (com *Compiler) Compile(query []byte, role string) (*QCode, error) {
|
||||
var err error
|
||||
|
||||
qc := QCode{Type: QTQuery}
|
||||
qc.Roots = qc.rootsA[:0]
|
||||
|
||||
op, err := Parse(query)
|
||||
if err != nil {
|
||||
@ -250,7 +253,7 @@ func (com *Compiler) Compile(query []byte, role string) (*QCode, error) {
|
||||
|
||||
func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||
id := int32(0)
|
||||
parentID := int32(0)
|
||||
parentID := int32(-1)
|
||||
|
||||
if len(op.Fields) == 0 {
|
||||
return errors.New("invalid graphql no query found")
|
||||
@ -269,7 +272,12 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||
if len(op.Fields) == 0 {
|
||||
return errors.New("empty query")
|
||||
}
|
||||
st.Push(op.Fields[0].ID)
|
||||
|
||||
for i := range op.Fields {
|
||||
if op.Fields[i].ParentID == -1 {
|
||||
st.Push(op.Fields[i].ID)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if st.Len() == 0 {
|
||||
@ -313,11 +321,6 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||
s.PresetList = trv.update.pslist
|
||||
}
|
||||
|
||||
if s.ID != 0 {
|
||||
p := &selects[s.ParentID]
|
||||
p.Children = append(p.Children, s.ID)
|
||||
}
|
||||
|
||||
if len(field.Alias) != 0 {
|
||||
s.FieldName = field.Alias
|
||||
} else {
|
||||
@ -329,6 +332,15 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Order is important addFilters must come after compileArgs
|
||||
if s.ParentID == -1 {
|
||||
qc.Roots = append(qc.Roots, s.ID)
|
||||
com.addFilters(qc, s, role)
|
||||
} else {
|
||||
p := &selects[s.ParentID]
|
||||
p.Children = append(p.Children, s.ID)
|
||||
}
|
||||
|
||||
s.Cols = make([]Column, 0, len(field.Children))
|
||||
action = QTQuery
|
||||
|
||||
@ -362,36 +374,40 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
|
||||
return errors.New("invalid query")
|
||||
}
|
||||
|
||||
var fil *Exp
|
||||
root := &selects[0]
|
||||
qc.Selects = selects[:id]
|
||||
return nil
|
||||
}
|
||||
|
||||
if trv, ok := com.tr[role][op.Fields[0].Name]; ok {
|
||||
func (com *Compiler) addFilters(qc *QCode, root *Select, role string) {
|
||||
var fil *Exp
|
||||
|
||||
if trv, ok := com.tr[role][root.Table]; ok {
|
||||
fil = trv.filter(qc.Type)
|
||||
}
|
||||
|
||||
if fil != nil {
|
||||
switch fil.Op {
|
||||
case OpNop:
|
||||
case OpFalse:
|
||||
root.Where = fil
|
||||
default:
|
||||
if root.Where != nil {
|
||||
ow := root.Where
|
||||
|
||||
root.Where = expPool.Get().(*Exp)
|
||||
root.Where.Reset()
|
||||
root.Where.Op = OpAnd
|
||||
root.Where.Children = root.Where.childrenA[:2]
|
||||
root.Where.Children[0] = fil
|
||||
root.Where.Children[1] = ow
|
||||
} else {
|
||||
root.Where = fil
|
||||
}
|
||||
}
|
||||
if fil == nil {
|
||||
return
|
||||
}
|
||||
|
||||
qc.Selects = selects[:id]
|
||||
return nil
|
||||
switch fil.Op {
|
||||
case OpNop:
|
||||
case OpFalse:
|
||||
root.Where = fil
|
||||
|
||||
default:
|
||||
if root.Where != nil {
|
||||
ow := root.Where
|
||||
|
||||
root.Where = expPool.Get().(*Exp)
|
||||
root.Where.Reset()
|
||||
root.Where.Op = OpAnd
|
||||
root.Where.Children = root.Where.childrenA[:2]
|
||||
root.Where.Children[0] = fil
|
||||
root.Where.Children[1] = ow
|
||||
} else {
|
||||
root.Where = fil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (com *Compiler) compileArgs(qc *QCode, sel *Select, args []Arg) error {
|
||||
|
Reference in New Issue
Block a user