Reduce allocations and improve perf over 50%
This commit is contained in:
7
qcode/bench.0
Normal file
7
qcode/bench.0
Normal file
@ -0,0 +1,7 @@
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
pkg: github.com/dosco/super-graph/qcode
|
||||
BenchmarkQCompile-8 100000 18528 ns/op 9208 B/op 107 allocs/op
|
||||
BenchmarkQCompileP-8 300000 5952 ns/op 9208 B/op 107 allocs/op
|
||||
PASS
|
||||
ok github.com/dosco/super-graph/qcode 3.893s
|
7
qcode/bench.1
Normal file
7
qcode/bench.1
Normal file
@ -0,0 +1,7 @@
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
pkg: github.com/dosco/super-graph/qcode
|
||||
BenchmarkQCompile-8 100000 18240 ns/op 7454 B/op 88 allocs/op
|
||||
BenchmarkQCompileP-8 300000 5788 ns/op 7494 B/op 88 allocs/op
|
||||
PASS
|
||||
ok github.com/dosco/super-graph/qcode 3.813s
|
7
qcode/bench.2
Normal file
7
qcode/bench.2
Normal file
@ -0,0 +1,7 @@
|
||||
goos: darwin
|
||||
goarch: amd64
|
||||
pkg: github.com/dosco/super-graph/qcode
|
||||
BenchmarkQCompile-8 100000 17231 ns/op 3352 B/op 87 allocs/op
|
||||
BenchmarkQCompileP-8 300000 5023 ns/op 3387 B/op 87 allocs/op
|
||||
PASS
|
||||
ok github.com/dosco/super-graph/qcode 3.462s
|
37
qcode/lex.go
37
qcode/lex.go
@ -103,13 +103,20 @@ type stateFn func(*lexer) stateFn
|
||||
|
||||
// lexer holds the state of the scanner.
|
||||
type lexer struct {
|
||||
name string // the name of the input; used only for error reports
|
||||
input string // the string being scanned
|
||||
pos Pos // current position in the input
|
||||
start Pos // start position of this item
|
||||
width Pos // width of last rune read from input
|
||||
items []item // array of scanned items
|
||||
line int // 1+number of newlines seen
|
||||
name string // the name of the input; used only for error reports
|
||||
input string // the string being scanned
|
||||
pos Pos // current position in the input
|
||||
start Pos // start position of this item
|
||||
width Pos // width of last rune read from input
|
||||
items []item // array of scanned items
|
||||
itemsA [100]item
|
||||
line int // 1+number of newlines seen
|
||||
}
|
||||
|
||||
var zeroLex = lexer{}
|
||||
|
||||
func (l *lexer) Reset() {
|
||||
*l = zeroLex
|
||||
}
|
||||
|
||||
// next returns the next rune in the input.
|
||||
@ -207,21 +214,19 @@ func (l *lexer) errorf(format string, args ...interface{}) stateFn {
|
||||
}
|
||||
|
||||
// lex creates a new scanner for the input string.
|
||||
func lex(input string) (*lexer, error) {
|
||||
func lex(l *lexer, input string) error {
|
||||
if len(input) == 0 {
|
||||
return nil, errors.New("empty query")
|
||||
}
|
||||
l := &lexer{
|
||||
input: input,
|
||||
items: make([]item, 0, 100),
|
||||
line: 1,
|
||||
return errors.New("empty query")
|
||||
}
|
||||
l.input = input
|
||||
l.line = 1
|
||||
l.items = l.itemsA[:0]
|
||||
l.run()
|
||||
|
||||
if last := l.items[len(l.items)-1]; last.typ == itemError {
|
||||
return nil, fmt.Errorf(last.val)
|
||||
return fmt.Errorf(last.val)
|
||||
}
|
||||
return l, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
// run runs the state machine for the lexer.
|
||||
|
208
qcode/parse.go
208
qcode/parse.go
@ -3,6 +3,7 @@ package qcode
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/dosco/super-graph/util"
|
||||
)
|
||||
@ -15,6 +16,7 @@ type parserType int16
|
||||
|
||||
const (
|
||||
maxFields = 100
|
||||
maxArgs = 10
|
||||
|
||||
parserError parserType = iota
|
||||
parserEOF
|
||||
@ -30,52 +32,30 @@ const (
|
||||
nodeVar
|
||||
)
|
||||
|
||||
func (t parserType) String() string {
|
||||
var v string
|
||||
|
||||
switch t {
|
||||
case parserEOF:
|
||||
v = "EOF"
|
||||
case parserError:
|
||||
v = "error"
|
||||
case opQuery:
|
||||
v = "query"
|
||||
case opMutate:
|
||||
v = "mutation"
|
||||
case opSub:
|
||||
v = "subscription"
|
||||
case nodeStr:
|
||||
v = "node-string"
|
||||
case nodeInt:
|
||||
v = "node-int"
|
||||
case nodeFloat:
|
||||
v = "node-float"
|
||||
case nodeBool:
|
||||
v = "node-bool"
|
||||
case nodeVar:
|
||||
v = "node-var"
|
||||
case nodeObj:
|
||||
v = "node-obj"
|
||||
case nodeList:
|
||||
v = "node-list"
|
||||
}
|
||||
return fmt.Sprintf("<%s>", v)
|
||||
type Operation struct {
|
||||
Type parserType
|
||||
Name string
|
||||
Args []Arg
|
||||
argsA [10]Arg
|
||||
Fields []Field
|
||||
fieldsA [10]Field
|
||||
}
|
||||
|
||||
type Operation struct {
|
||||
Type parserType
|
||||
Name string
|
||||
Args []*Arg
|
||||
Fields []Field
|
||||
var zeroOperation = Operation{}
|
||||
|
||||
func (o *Operation) Reset() {
|
||||
*o = zeroOperation
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
ID uint16
|
||||
Name string
|
||||
Alias string
|
||||
Args []*Arg
|
||||
ParentID uint16
|
||||
Children []uint16
|
||||
ID uint16
|
||||
Name string
|
||||
Alias string
|
||||
Args []Arg
|
||||
argsA [10]Arg
|
||||
ParentID uint16
|
||||
Children []uint16
|
||||
childrenA [10]uint16
|
||||
}
|
||||
|
||||
type Arg struct {
|
||||
@ -91,6 +71,12 @@ type Node struct {
|
||||
Children []*Node
|
||||
}
|
||||
|
||||
var zeroNode = Node{}
|
||||
|
||||
func (n *Node) Reset() {
|
||||
*n = zeroNode
|
||||
}
|
||||
|
||||
type Parser struct {
|
||||
pos int
|
||||
items []item
|
||||
@ -98,20 +84,36 @@ type Parser struct {
|
||||
err error
|
||||
}
|
||||
|
||||
var nodePool = sync.Pool{
|
||||
New: func() interface{} { return new(Node) },
|
||||
}
|
||||
|
||||
var opPool = sync.Pool{
|
||||
New: func() interface{} { return new(Operation) },
|
||||
}
|
||||
|
||||
var lexPool = sync.Pool{
|
||||
New: func() interface{} { return new(lexer) },
|
||||
}
|
||||
|
||||
func Parse(gql string) (*Operation, error) {
|
||||
if len(gql) == 0 {
|
||||
return nil, errors.New("blank query")
|
||||
}
|
||||
l := lexPool.Get().(*lexer)
|
||||
l.Reset()
|
||||
|
||||
l, err := lex(gql)
|
||||
if err != nil {
|
||||
if err := lex(l, gql); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &Parser{
|
||||
pos: -1,
|
||||
items: l.items,
|
||||
}
|
||||
return p.parseOp()
|
||||
op, err := p.parseOp()
|
||||
lexPool.Put(l)
|
||||
|
||||
return op, err
|
||||
}
|
||||
|
||||
func ParseQuery(gql string) (*Operation, error) {
|
||||
@ -119,28 +121,37 @@ func ParseQuery(gql string) (*Operation, error) {
|
||||
}
|
||||
|
||||
func ParseArgValue(argVal string) (*Node, error) {
|
||||
l, err := lex(argVal)
|
||||
if err != nil {
|
||||
l := lexPool.Get().(*lexer)
|
||||
l.Reset()
|
||||
|
||||
if err := lex(l, argVal); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &Parser{
|
||||
pos: -1,
|
||||
items: l.items,
|
||||
}
|
||||
op, err := p.parseValue()
|
||||
lexPool.Put(l)
|
||||
|
||||
return p.parseValue()
|
||||
return op, err
|
||||
}
|
||||
|
||||
func parseByType(gql string, ty parserType) (*Operation, error) {
|
||||
l, err := lex(gql)
|
||||
if err != nil {
|
||||
l := lexPool.Get().(*lexer)
|
||||
l.Reset()
|
||||
|
||||
if err := lex(l, gql); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p := &Parser{
|
||||
pos: -1,
|
||||
items: l.items,
|
||||
}
|
||||
return p.parseOpByType(ty)
|
||||
op, err := p.parseOpByType(ty)
|
||||
lexPool.Put(l)
|
||||
|
||||
return op, err
|
||||
}
|
||||
|
||||
func (p *Parser) next() item {
|
||||
@ -188,7 +199,13 @@ func (p *Parser) peek(types ...itemType) bool {
|
||||
}
|
||||
|
||||
func (p *Parser) parseOpByType(ty parserType) (*Operation, error) {
|
||||
op := &Operation{Type: ty}
|
||||
op := opPool.Get().(*Operation)
|
||||
op.Reset()
|
||||
|
||||
op.Type = ty
|
||||
op.Fields = op.fieldsA[:0]
|
||||
op.Args = op.argsA[:0]
|
||||
|
||||
var err error
|
||||
|
||||
if p.peek(itemName) {
|
||||
@ -197,7 +214,7 @@ func (p *Parser) parseOpByType(ty parserType) (*Operation, error) {
|
||||
|
||||
if p.peek(itemArgsOpen) {
|
||||
p.ignore()
|
||||
op.Args, err = p.parseArgs()
|
||||
op.Args, err = p.parseArgs(op.Args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -205,7 +222,7 @@ func (p *Parser) parseOpByType(ty parserType) (*Operation, error) {
|
||||
|
||||
if p.peek(itemObjOpen) {
|
||||
p.ignore()
|
||||
op.Fields, err = p.parseFields()
|
||||
op.Fields, err = p.parseFields(op.Fields)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -238,15 +255,12 @@ func (p *Parser) parseOp() (*Operation, error) {
|
||||
return nil, errors.New("unknown operation type")
|
||||
}
|
||||
|
||||
func (p *Parser) parseFields() ([]Field, error) {
|
||||
var id uint16
|
||||
|
||||
fields := make([]Field, 0, 5)
|
||||
func (p *Parser) parseFields(fields []Field) ([]Field, error) {
|
||||
st := util.NewStack()
|
||||
|
||||
for {
|
||||
if id >= maxFields {
|
||||
return nil, fmt.Errorf("field limit reached (%d)", maxFields)
|
||||
if len(fields) >= maxFields {
|
||||
return nil, fmt.Errorf("too many fields (max %d)", maxFields)
|
||||
}
|
||||
|
||||
if p.peek(itemObjClose) {
|
||||
@ -263,9 +277,12 @@ func (p *Parser) parseFields() ([]Field, error) {
|
||||
return nil, errors.New("expecting an alias or field name")
|
||||
}
|
||||
|
||||
f := Field{ID: id}
|
||||
fields = append(fields, Field{ID: uint16(len(fields))})
|
||||
f := &fields[(len(fields) - 1)]
|
||||
f.Args = f.argsA[:0]
|
||||
f.Children = f.childrenA[:0]
|
||||
|
||||
if err := p.parseField(&f); err != nil {
|
||||
if err := p.parseField(f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -281,9 +298,6 @@ func (p *Parser) parseFields() ([]Field, error) {
|
||||
fields[pid].Children = append(fields[pid].Children, f.ID)
|
||||
}
|
||||
|
||||
fields = append(fields, f)
|
||||
id++
|
||||
|
||||
if p.peek(itemObjOpen) {
|
||||
p.ignore()
|
||||
st.Push(f.ID)
|
||||
@ -310,7 +324,7 @@ func (p *Parser) parseField(f *Field) error {
|
||||
|
||||
if p.peek(itemArgsOpen) {
|
||||
p.ignore()
|
||||
if f.Args, err = p.parseArgs(); err != nil {
|
||||
if f.Args, err = p.parseArgs(f.Args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -318,11 +332,14 @@ func (p *Parser) parseField(f *Field) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseArgs() ([]*Arg, error) {
|
||||
var args []*Arg
|
||||
func (p *Parser) parseArgs(args []Arg) ([]Arg, error) {
|
||||
var err error
|
||||
|
||||
for {
|
||||
if len(args) >= maxArgs {
|
||||
return nil, fmt.Errorf("too many args (max %d)", maxArgs)
|
||||
}
|
||||
|
||||
if p.peek(itemArgsClose) {
|
||||
p.ignore()
|
||||
break
|
||||
@ -330,7 +347,8 @@ func (p *Parser) parseArgs() ([]*Arg, error) {
|
||||
if p.peek(itemName) == false {
|
||||
return nil, errors.New("expecting an argument name")
|
||||
}
|
||||
arg := &Arg{Name: p.next().val}
|
||||
args = append(args, Arg{Name: p.next().val})
|
||||
arg := &args[(len(args) - 1)]
|
||||
|
||||
if p.peek(itemColon) == false {
|
||||
return nil, errors.New("missing ':' after argument name")
|
||||
@ -341,16 +359,17 @@ func (p *Parser) parseArgs() ([]*Arg, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
args = append(args, arg)
|
||||
}
|
||||
return args, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseList() (*Node, error) {
|
||||
parent := &Node{}
|
||||
var nodes []*Node
|
||||
var ty parserType
|
||||
nodes := []*Node{}
|
||||
|
||||
parent := nodePool.Get().(*Node)
|
||||
parent.Reset()
|
||||
|
||||
var ty parserType
|
||||
for {
|
||||
if p.peek(itemListClose) {
|
||||
p.ignore()
|
||||
@ -381,8 +400,10 @@ func (p *Parser) parseList() (*Node, error) {
|
||||
}
|
||||
|
||||
func (p *Parser) parseObj() (*Node, error) {
|
||||
parent := &Node{}
|
||||
var nodes []*Node
|
||||
nodes := []*Node{}
|
||||
|
||||
parent := nodePool.Get().(*Node)
|
||||
parent.Reset()
|
||||
|
||||
for {
|
||||
if p.peek(itemObjClose) {
|
||||
@ -427,7 +448,8 @@ func (p *Parser) parseValue() (*Node, error) {
|
||||
}
|
||||
|
||||
item := p.next()
|
||||
node := &Node{}
|
||||
node := nodePool.Get().(*Node)
|
||||
node.Reset()
|
||||
|
||||
switch item.typ {
|
||||
case itemIntVal:
|
||||
@ -449,3 +471,39 @@ func (p *Parser) parseValue() (*Node, error) {
|
||||
|
||||
return node, nil
|
||||
}
|
||||
|
||||
func (t parserType) String() string {
|
||||
var v string
|
||||
|
||||
switch t {
|
||||
case parserEOF:
|
||||
v = "EOF"
|
||||
case parserError:
|
||||
v = "error"
|
||||
case opQuery:
|
||||
v = "query"
|
||||
case opMutate:
|
||||
v = "mutation"
|
||||
case opSub:
|
||||
v = "subscription"
|
||||
case nodeStr:
|
||||
v = "node-string"
|
||||
case nodeInt:
|
||||
v = "node-int"
|
||||
case nodeFloat:
|
||||
v = "node-float"
|
||||
case nodeBool:
|
||||
v = "node-bool"
|
||||
case nodeVar:
|
||||
v = "node-var"
|
||||
case nodeObj:
|
||||
v = "node-obj"
|
||||
case nodeList:
|
||||
v = "node-list"
|
||||
}
|
||||
return fmt.Sprintf("<%s>", v)
|
||||
}
|
||||
|
||||
func PutNode(n *Node) {
|
||||
nodePool.Put(n)
|
||||
}
|
||||
|
@ -74,27 +74,36 @@ func TestEmptyCompile(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
var gql = `query {
|
||||
products(
|
||||
# returns only 30 items
|
||||
limit: 30,
|
||||
|
||||
# starts from item 10, commented out for now
|
||||
# offset: 10,
|
||||
|
||||
# orders the response items by highest price
|
||||
order_by: { price: desc },
|
||||
|
||||
# no duplicate prices returned
|
||||
distinct: [ price ]
|
||||
|
||||
# only items with an id >= 30 and < 30 are returned
|
||||
where: { id: { AND: { greater_or_equals: 20, lt: 28 } } }) {
|
||||
id
|
||||
name
|
||||
price
|
||||
}
|
||||
}`
|
||||
|
||||
func BenchmarkQCompile(b *testing.B) {
|
||||
qcompile, _ := NewCompiler(Config{})
|
||||
|
||||
val := `query {
|
||||
products(
|
||||
where: {
|
||||
and: {
|
||||
not: { id: { is_null: true } },
|
||||
price: { gt: 10 }
|
||||
}}) {
|
||||
id
|
||||
name
|
||||
price
|
||||
}
|
||||
}`
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := qcompile.CompileQuery(val)
|
||||
_, err := qcompile.CompileQuery(gql)
|
||||
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@ -102,28 +111,19 @@ func BenchmarkQCompile(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLex(b *testing.B) {
|
||||
val := `query {
|
||||
products(
|
||||
where: {
|
||||
and: {
|
||||
not: { id: { is_null: true } },
|
||||
price: { gt: 10 }
|
||||
}}) {
|
||||
id
|
||||
name
|
||||
price
|
||||
}
|
||||
}`
|
||||
func BenchmarkQCompileP(b *testing.B) {
|
||||
qcompile, _ := NewCompiler(Config{})
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := lex(val)
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
_, err := qcompile.CompileQuery(gql)
|
||||
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
3
qcode/pprof_cpu.sh
Executable file
3
qcode/pprof_cpu.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
go test -bench=. -benchmem -cpuprofile cpu.out -run=XXX
|
||||
go tool pprof -cum cpu.out
|
3
qcode/pprof_mem.sh
Executable file
3
qcode/pprof_mem.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
go test -bench=. -benchmem -memprofile mem.out -run=XXX
|
||||
go tool pprof -cum mem.out
|
215
qcode/qcode.go
215
qcode/qcode.go
@ -97,66 +97,6 @@ const (
|
||||
OpTsQuery
|
||||
)
|
||||
|
||||
func (t ExpOp) String() string {
|
||||
var v string
|
||||
|
||||
switch t {
|
||||
case OpNop:
|
||||
v = "op-nop"
|
||||
case OpAnd:
|
||||
v = "op-and"
|
||||
case OpOr:
|
||||
v = "op-or"
|
||||
case OpNot:
|
||||
v = "op-not"
|
||||
case OpEquals:
|
||||
v = "op-equals"
|
||||
case OpNotEquals:
|
||||
v = "op-not-equals"
|
||||
case OpGreaterOrEquals:
|
||||
v = "op-greater-or-equals"
|
||||
case OpLesserOrEquals:
|
||||
v = "op-lesser-or-equals"
|
||||
case OpGreaterThan:
|
||||
v = "op-greater-than"
|
||||
case OpLesserThan:
|
||||
v = "op-lesser-than"
|
||||
case OpIn:
|
||||
v = "op-in"
|
||||
case OpNotIn:
|
||||
v = "op-not-in"
|
||||
case OpLike:
|
||||
v = "op-like"
|
||||
case OpNotLike:
|
||||
v = "op-not-like"
|
||||
case OpILike:
|
||||
v = "op-i-like"
|
||||
case OpNotILike:
|
||||
v = "op-not-i-like"
|
||||
case OpSimilar:
|
||||
v = "op-similar"
|
||||
case OpNotSimilar:
|
||||
v = "op-not-similar"
|
||||
case OpContains:
|
||||
v = "op-contains"
|
||||
case OpContainedIn:
|
||||
v = "op-contained-in"
|
||||
case OpHasKey:
|
||||
v = "op-has-key"
|
||||
case OpHasKeyAny:
|
||||
v = "op-has-key-any"
|
||||
case OpHasKeyAll:
|
||||
v = "op-has-key-all"
|
||||
case OpIsNull:
|
||||
v = "op-is-null"
|
||||
case OpEqID:
|
||||
v = "op-eq-id"
|
||||
case OpTsQuery:
|
||||
v = "op-ts-query"
|
||||
}
|
||||
return fmt.Sprintf("<%s>", v)
|
||||
}
|
||||
|
||||
type ValType int
|
||||
|
||||
const (
|
||||
@ -194,29 +134,31 @@ type Config struct {
|
||||
DefaultFilter []string
|
||||
FilterMap map[string][]string
|
||||
Blacklist []string
|
||||
KeepArgs bool
|
||||
}
|
||||
|
||||
type Compiler struct {
|
||||
fl *Exp
|
||||
fm map[string]*Exp
|
||||
bl map[string]struct{}
|
||||
ka bool
|
||||
}
|
||||
|
||||
func NewCompiler(conf Config) (*Compiler, error) {
|
||||
bl := make(map[string]struct{}, len(conf.Blacklist))
|
||||
func NewCompiler(c Config) (*Compiler, error) {
|
||||
bl := make(map[string]struct{}, len(c.Blacklist))
|
||||
|
||||
for i := range conf.Blacklist {
|
||||
bl[strings.ToLower(conf.Blacklist[i])] = struct{}{}
|
||||
for i := range c.Blacklist {
|
||||
bl[strings.ToLower(c.Blacklist[i])] = struct{}{}
|
||||
}
|
||||
|
||||
fl, err := compileFilter(conf.DefaultFilter)
|
||||
fl, err := compileFilter(c.DefaultFilter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fm := make(map[string]*Exp, len(conf.FilterMap))
|
||||
fm := make(map[string]*Exp, len(c.FilterMap))
|
||||
|
||||
for k, v := range conf.FilterMap {
|
||||
for k, v := range c.FilterMap {
|
||||
fil, err := compileFilter(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -224,7 +166,7 @@ func NewCompiler(conf Config) (*Compiler, error) {
|
||||
fm[strings.ToLower(k)] = fil
|
||||
}
|
||||
|
||||
return &Compiler{fl, fm, bl}, nil
|
||||
return &Compiler{fl, fm, bl, c.KeepArgs}, nil
|
||||
}
|
||||
|
||||
func (com *Compiler) CompileQuery(query string) (*QCode, error) {
|
||||
@ -249,6 +191,8 @@ func (com *Compiler) CompileQuery(query string) (*QCode, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opPool.Put(op)
|
||||
|
||||
return &qc, nil
|
||||
}
|
||||
|
||||
@ -383,44 +327,46 @@ func (com *Compiler) compileQuery(op *Operation) (*Query, error) {
|
||||
return &Query{selects[:id]}, nil
|
||||
}
|
||||
|
||||
func (com *Compiler) compileArgs(sel *Select, args []*Arg) error {
|
||||
func (com *Compiler) compileArgs(sel *Select, args []Arg) error {
|
||||
var err error
|
||||
|
||||
sel.Args = make(map[string]*Node, len(args))
|
||||
if com.ka {
|
||||
sel.Args = make(map[string]*Node, len(args))
|
||||
}
|
||||
|
||||
for i := range args {
|
||||
if args[i] == nil {
|
||||
return fmt.Errorf("[Args] unexpected nil argument found")
|
||||
}
|
||||
an := strings.ToLower(args[i].Name)
|
||||
if _, ok := sel.Args[an]; ok {
|
||||
continue
|
||||
}
|
||||
arg := &args[i]
|
||||
|
||||
an := strings.ToLower(arg.Name)
|
||||
|
||||
switch an {
|
||||
case "id":
|
||||
if sel.ID == 0 {
|
||||
err = com.compileArgID(sel, args[i])
|
||||
err = com.compileArgID(sel, arg)
|
||||
}
|
||||
case "search":
|
||||
err = com.compileArgSearch(sel, args[i])
|
||||
err = com.compileArgSearch(sel, arg)
|
||||
case "where":
|
||||
err = com.compileArgWhere(sel, args[i])
|
||||
err = com.compileArgWhere(sel, arg)
|
||||
case "orderby", "order_by", "order":
|
||||
err = com.compileArgOrderBy(sel, args[i])
|
||||
err = com.compileArgOrderBy(sel, arg)
|
||||
case "distinct_on", "distinct":
|
||||
err = com.compileArgDistinctOn(sel, args[i])
|
||||
err = com.compileArgDistinctOn(sel, arg)
|
||||
case "limit":
|
||||
err = com.compileArgLimit(sel, args[i])
|
||||
err = com.compileArgLimit(sel, arg)
|
||||
case "offset":
|
||||
err = com.compileArgOffset(sel, args[i])
|
||||
err = com.compileArgOffset(sel, arg)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sel.Args[an] = args[i].Val
|
||||
if sel.Args != nil {
|
||||
sel.Args[an] = arg.Val
|
||||
} else {
|
||||
nodePool.Put(arg.Val)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -439,15 +385,15 @@ func (com *Compiler) compileArgObj(arg *Arg) (*Exp, error) {
|
||||
return com.compileArgNode(arg.Val)
|
||||
}
|
||||
|
||||
func (com *Compiler) compileArgNode(val *Node) (*Exp, error) {
|
||||
func (com *Compiler) compileArgNode(node *Node) (*Exp, error) {
|
||||
st := util.NewStack()
|
||||
var root *Exp
|
||||
|
||||
if val == nil || len(val.Children) == 0 {
|
||||
if node == nil || len(node.Children) == 0 {
|
||||
return nil, errors.New("invalid argument value")
|
||||
}
|
||||
|
||||
st.Push(&expT{nil, val.Children[0]})
|
||||
st.Push(&expT{nil, node.Children[0]})
|
||||
|
||||
for {
|
||||
if st.Len() == 0 {
|
||||
@ -483,6 +429,25 @@ func (com *Compiler) compileArgNode(val *Node) (*Exp, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if com.ka {
|
||||
return root, nil
|
||||
}
|
||||
|
||||
st.Push(node.Children[0])
|
||||
|
||||
for {
|
||||
if st.Len() == 0 {
|
||||
break
|
||||
}
|
||||
intf := st.Pop()
|
||||
node, _ := intf.(*Node)
|
||||
|
||||
for i := range node.Children {
|
||||
st.Push(node.Children[i])
|
||||
}
|
||||
nodePool.Put(node)
|
||||
}
|
||||
|
||||
return root, nil
|
||||
}
|
||||
|
||||
@ -566,6 +531,9 @@ func (com *Compiler) compileArgOrderBy(sel *Select, arg *Arg) error {
|
||||
}
|
||||
|
||||
if _, ok := com.bl[strings.ToLower(node.Name)]; ok {
|
||||
if !com.ka {
|
||||
nodePool.Put(node)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@ -573,6 +541,9 @@ func (com *Compiler) compileArgOrderBy(sel *Select, arg *Arg) error {
|
||||
for i := range node.Children {
|
||||
st.Push(node.Children[i])
|
||||
}
|
||||
if !com.ka {
|
||||
nodePool.Put(node)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
@ -598,6 +569,10 @@ func (com *Compiler) compileArgOrderBy(sel *Select, arg *Arg) error {
|
||||
|
||||
setOrderByColName(ob, node)
|
||||
sel.OrderBy = append(sel.OrderBy, ob)
|
||||
|
||||
if !com.ka {
|
||||
nodePool.Put(node)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -619,6 +594,9 @@ func (com *Compiler) compileArgDistinctOn(sel *Select, arg *Arg) error {
|
||||
|
||||
for i := range node.Children {
|
||||
sel.DistinctOn = append(sel.DistinctOn, node.Children[i].Val)
|
||||
if !com.ka {
|
||||
nodePool.Put(node.Children[i])
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@ -644,7 +622,6 @@ func (com *Compiler) compileArgOffset(sel *Select, arg *Arg) error {
|
||||
}
|
||||
|
||||
sel.Paging.Offset = node.Val
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -887,3 +864,63 @@ func relID(h *xxhash.Digest, child, parent string) uint64 {
|
||||
h.Reset()
|
||||
return v
|
||||
}
|
||||
|
||||
func (t ExpOp) String() string {
|
||||
var v string
|
||||
|
||||
switch t {
|
||||
case OpNop:
|
||||
v = "op-nop"
|
||||
case OpAnd:
|
||||
v = "op-and"
|
||||
case OpOr:
|
||||
v = "op-or"
|
||||
case OpNot:
|
||||
v = "op-not"
|
||||
case OpEquals:
|
||||
v = "op-equals"
|
||||
case OpNotEquals:
|
||||
v = "op-not-equals"
|
||||
case OpGreaterOrEquals:
|
||||
v = "op-greater-or-equals"
|
||||
case OpLesserOrEquals:
|
||||
v = "op-lesser-or-equals"
|
||||
case OpGreaterThan:
|
||||
v = "op-greater-than"
|
||||
case OpLesserThan:
|
||||
v = "op-lesser-than"
|
||||
case OpIn:
|
||||
v = "op-in"
|
||||
case OpNotIn:
|
||||
v = "op-not-in"
|
||||
case OpLike:
|
||||
v = "op-like"
|
||||
case OpNotLike:
|
||||
v = "op-not-like"
|
||||
case OpILike:
|
||||
v = "op-i-like"
|
||||
case OpNotILike:
|
||||
v = "op-not-i-like"
|
||||
case OpSimilar:
|
||||
v = "op-similar"
|
||||
case OpNotSimilar:
|
||||
v = "op-not-similar"
|
||||
case OpContains:
|
||||
v = "op-contains"
|
||||
case OpContainedIn:
|
||||
v = "op-contained-in"
|
||||
case OpHasKey:
|
||||
v = "op-has-key"
|
||||
case OpHasKeyAny:
|
||||
v = "op-has-key-any"
|
||||
case OpHasKeyAll:
|
||||
v = "op-has-key-all"
|
||||
case OpIsNull:
|
||||
v = "op-is-null"
|
||||
case OpEqID:
|
||||
v = "op-eq-id"
|
||||
case OpTsQuery:
|
||||
v = "op-ts-query"
|
||||
}
|
||||
return fmt.Sprintf("<%s>", v)
|
||||
}
|
||||
|
Reference in New Issue
Block a user