From 98c595312af02037843b8fe74f0ee0625665448e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Aasted=20S=C3=B8rensen?= Date: Thu, 7 Nov 2019 08:30:50 +0100 Subject: [PATCH] libs/pubsub/query: add EXISTS operator (#4077) ## Issue: This PR adds an "EXISTS" condition to the event query grammar. It enables querying for the occurrence of an event without having to provide a condition for one of its attributes. As an example, someone interested in all slashing events might currently catch them with a query such as slash.power > 0. With this PR the event can be captured with slash.power EXISTS or just slash EXISTS to catch by event type. ## Examples: `slash EXISTS` ## Commits: * Add EXISTS condition to query grammar * Gofmt files * Move PEG instructions out of auto-generated file to prevent overwrite * Update libs/pubsub/query/query.go Co-Authored-By: Anton Kaliaev * Update changelog and add test case * Merge with other changes in PR #4070 * Add EXISTS to Conditions() func * Apply gofmt * Addressing PR comments --- CHANGELOG_PENDING.md | 1 + libs/pubsub/query/parser_test.go | 5 + libs/pubsub/query/peg.go | 4 + libs/pubsub/query/query.go | 37 +- libs/pubsub/query/query.peg | 2 + libs/pubsub/query/query.peg.go | 1396 ++++++++++++++++++------------ libs/pubsub/query/query_test.go | 45 +- rpc/swagger/swagger.yaml | 4 +- 8 files changed, 947 insertions(+), 547 deletions(-) create mode 100644 libs/pubsub/query/peg.go diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index ce21dbac8..2deb3a910 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -27,6 +27,7 @@ program](https://hackerone.com/tendermint). - [libs/pubsub] [\#4070](https://github.com/tendermint/tendermint/pull/4070) No longer panic in `Query#(Matches|Conditions)` preferring to return an error instead. - [libs/pubsub] [\#4070](https://github.com/tendermint/tendermint/pull/4070) Strip out non-numeric characters when attempting to match numeric values. - [p2p] [\#3991](https://github.com/tendermint/tendermint/issues/3991) Log "has been established or dialed" as debug log instead of Error for connected peers (@whunmr) +- [rpc] [\#4077](https://github.com/tendermint/tendermint/pull/4077) Added support for `EXISTS` clause to the Websocket query interface. ### BUG FIXES: diff --git a/libs/pubsub/query/parser_test.go b/libs/pubsub/query/parser_test.go index 708dee484..a08a0d16d 100644 --- a/libs/pubsub/query/parser_test.go +++ b/libs/pubsub/query/parser_test.go @@ -77,6 +77,11 @@ func TestParser(t *testing.T) { {"account.balance >>= 400", false}, {"account.balance=33.22.1", false}, + {"slashing.amount EXISTS", true}, + {"slashing.amount EXISTS AND account.balance=100", true}, + {"account.balance=100 AND slashing.amount EXISTS", true}, + {"slashing EXISTS", true}, + {"hash='136E18F7E4C348B780CF873A0BF43922E5BAFA63'", true}, {"hash=136E18F7E4C348B780CF873A0BF43922E5BAFA63", false}, } diff --git a/libs/pubsub/query/peg.go b/libs/pubsub/query/peg.go new file mode 100644 index 000000000..3444dab34 --- /dev/null +++ b/libs/pubsub/query/peg.go @@ -0,0 +1,4 @@ +// nolint +package query + +//go:generate peg -inline -switch query.peg diff --git a/libs/pubsub/query/query.go b/libs/pubsub/query/query.go index 6d423dfeb..f59cb3904 100644 --- a/libs/pubsub/query/query.go +++ b/libs/pubsub/query/query.go @@ -80,6 +80,8 @@ const ( OpEqual // "CONTAINS"; used to check if a string contains a certain sub string. OpContains + // "EXISTS"; used to check if a certain event attribute is present. + OpExists ) const ( @@ -100,8 +102,8 @@ func (q *Query) Conditions() ([]Condition, error) { conditions := make([]Condition, 0) buffer, begin, end := q.parser.Buffer, 0, 0 - // tokens must be in the following order: event attribute ("tx.gas") -> operator ("=") -> operand ("7") - for _, token := range q.parser.Tokens() { + // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7") + for token := range q.parser.Tokens() { switch token.pegRule { case rulePegText: begin, end = int(token.begin), int(token.end) @@ -127,6 +129,10 @@ func (q *Query) Conditions() ([]Condition, error) { case rulecontains: op = OpContains + case ruleexists: + op = OpExists + conditions = append(conditions, Condition{eventAttr, op, nil}) + case rulevalue: // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock") valueWithoutSingleQuotes := buffer[begin+1 : end-1] @@ -207,8 +213,9 @@ func (q *Query) Matches(events map[string][]string) (bool, error) { buffer, begin, end := q.parser.Buffer, 0, 0 // tokens must be in the following order: - // event attribute ("tx.gas") -> operator ("=") -> operand ("7") - for _, token := range q.parser.Tokens() { + + // tag ("tx.gas") -> operator ("=") -> operand ("7") + for token := range q.parser.Tokens() { switch token.pegRule { case rulePegText: begin, end = int(token.begin), int(token.end) @@ -233,6 +240,28 @@ func (q *Query) Matches(events map[string][]string) (bool, error) { case rulecontains: op = OpContains + case ruleexists: + op = OpExists + if strings.Contains(eventAttr, ".") { + // Searching for a full "type.attribute" event. + _, ok := events[eventAttr] + if !ok { + return false, nil + } + } else { + foundEvent := false + + loop: + for compositeKey := range events { + if strings.Index(compositeKey, eventAttr) == 0 { + foundEvent = true + break loop + } + } + if !foundEvent { + return false, nil + } + } case rulevalue: // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock") diff --git a/libs/pubsub/query/query.peg b/libs/pubsub/query/query.peg index 739892e4f..e2cfd0826 100644 --- a/libs/pubsub/query/query.peg +++ b/libs/pubsub/query/query.peg @@ -11,6 +11,7 @@ condition <- tag ' '* (le ' '* (number / time / date) / g ' '* (number / time / date) / equal ' '* (number / time / date / value) / contains ' '* value + / exists ) tag <- < (![ \t\n\r\\()"'=><] .)+ > @@ -27,6 +28,7 @@ and <- "AND" equal <- "=" contains <- "CONTAINS" +exists <- "EXISTS" le <- "<=" ge <- ">=" l <- "<" diff --git a/libs/pubsub/query/query.peg.go b/libs/pubsub/query/query.peg.go index c1cc60aa9..a8e14c869 100644 --- a/libs/pubsub/query/query.peg.go +++ b/libs/pubsub/query/query.peg.go @@ -1,8 +1,6 @@ // nolint package query -//go:generate peg -inline -switch query.peg - import ( "fmt" "math" @@ -31,11 +29,16 @@ const ( ruleand ruleequal rulecontains + ruleexists rulele rulege rulel ruleg rulePegText + + rulePre + ruleIn + ruleSuf ) var rul3s = [...]string{ @@ -54,20 +57,16 @@ var rul3s = [...]string{ "and", "equal", "contains", + "exists", "le", "ge", "l", "g", "PegText", -} -type token32 struct { - pegRule - begin, end uint32 -} - -func (t *token32) String() string { - return fmt.Sprintf("\x1B[34m%v\x1B[m %v %v", rul3s[t.pegRule], t.begin, t.end) + "Pre_", + "_In_", + "_Suf", } type node32 struct { @@ -75,43 +74,57 @@ type node32 struct { up, next *node32 } -func (node *node32) print(pretty bool, buffer string) { - var print func(node *node32, depth int) - print = func(node *node32, depth int) { - for node != nil { - for c := 0; c < depth; c++ { - fmt.Printf(" ") - } - rule := rul3s[node.pegRule] - quote := strconv.Quote(string(([]rune(buffer)[node.begin:node.end]))) - if !pretty { - fmt.Printf("%v %v\n", rule, quote) - } else { - fmt.Printf("\x1B[34m%v\x1B[m %v\n", rule, quote) - } - if node.up != nil { - print(node.up, depth+1) - } - node = node.next +func (node *node32) print(depth int, buffer string) { + for node != nil { + for c := 0; c < depth; c++ { + fmt.Printf(" ") } + fmt.Printf("\x1B[34m%v\x1B[m %v\n", rul3s[node.pegRule], strconv.Quote(string(([]rune(buffer)[node.begin:node.end])))) + if node.up != nil { + node.up.print(depth+1, buffer) + } + node = node.next } - print(node, 0) } func (node *node32) Print(buffer string) { - node.print(false, buffer) + node.print(0, buffer) } -func (node *node32) PrettyPrint(buffer string) { - node.print(true, buffer) +type element struct { + node *node32 + down *element +} + +/* ${@} bit structure for abstract syntax tree */ +type token32 struct { + pegRule + begin, end, next uint32 +} + +func (t *token32) isZero() bool { + return t.pegRule == ruleUnknown && t.begin == 0 && t.end == 0 && t.next == 0 +} + +func (t *token32) isParentOf(u token32) bool { + return t.begin <= u.begin && t.end >= u.end && t.next > u.next +} + +func (t *token32) getToken32() token32 { + return token32{pegRule: t.pegRule, begin: uint32(t.begin), end: uint32(t.end), next: uint32(t.next)} +} + +func (t *token32) String() string { + return fmt.Sprintf("\x1B[34m%v\x1B[m %v %v %v", rul3s[t.pegRule], t.begin, t.end, t.next) } type tokens32 struct { - tree []token32 + tree []token32 + ordered [][]token32 } -func (t *tokens32) Trim(length uint32) { - t.tree = t.tree[:length] +func (t *tokens32) trim(length int) { + t.tree = t.tree[0:length] } func (t *tokens32) Print() { @@ -120,14 +133,51 @@ func (t *tokens32) Print() { } } -func (t *tokens32) AST() *node32 { - type element struct { - node *node32 - down *element +func (t *tokens32) Order() [][]token32 { + if t.ordered != nil { + return t.ordered } + + depths := make([]int32, 1, math.MaxInt16) + for i, token := range t.tree { + if token.pegRule == ruleUnknown { + t.tree = t.tree[:i] + break + } + depth := int(token.next) + if length := len(depths); depth >= length { + depths = depths[:depth+1] + } + depths[depth]++ + } + depths = append(depths, 0) + + ordered, pool := make([][]token32, len(depths)), make([]token32, len(t.tree)+len(depths)) + for i, depth := range depths { + depth++ + ordered[i], pool, depths[i] = pool[:depth], pool[depth:], 0 + } + + for i, token := range t.tree { + depth := token.next + token.next = uint32(i) + ordered[depth][depths[depth]] = token + depths[depth]++ + } + t.ordered = ordered + return ordered +} + +type state32 struct { + token32 + depths []int32 + leaf bool +} + +func (t *tokens32) AST() *node32 { tokens := t.Tokens() - var stack *element - for _, token := range tokens { + stack := &element{node: &node32{token32: <-tokens}} + for token := range tokens { if token.begin == token.end { continue } @@ -139,55 +189,180 @@ func (t *tokens32) AST() *node32 { } stack = &element{node: node, down: stack} } - if stack != nil { - return stack.node + return stack.node +} + +func (t *tokens32) PreOrder() (<-chan state32, [][]token32) { + s, ordered := make(chan state32, 6), t.Order() + go func() { + var states [8]state32 + for i := range states { + states[i].depths = make([]int32, len(ordered)) + } + depths, state, depth := make([]int32, len(ordered)), 0, 1 + write := func(t token32, leaf bool) { + S := states[state] + state, S.pegRule, S.begin, S.end, S.next, S.leaf = (state+1)%8, t.pegRule, t.begin, t.end, uint32(depth), leaf + copy(S.depths, depths) + s <- S + } + + states[state].token32 = ordered[0][0] + depths[0]++ + state++ + a, b := ordered[depth-1][depths[depth-1]-1], ordered[depth][depths[depth]] + depthFirstSearch: + for { + for { + if i := depths[depth]; i > 0 { + if c, j := ordered[depth][i-1], depths[depth-1]; a.isParentOf(c) && + (j < 2 || !ordered[depth-1][j-2].isParentOf(c)) { + if c.end != b.begin { + write(token32{pegRule: ruleIn, begin: c.end, end: b.begin}, true) + } + break + } + } + + if a.begin < b.begin { + write(token32{pegRule: rulePre, begin: a.begin, end: b.begin}, true) + } + break + } + + next := depth + 1 + if c := ordered[next][depths[next]]; c.pegRule != ruleUnknown && b.isParentOf(c) { + write(b, false) + depths[depth]++ + depth, a, b = next, b, c + continue + } + + write(b, true) + depths[depth]++ + c, parent := ordered[depth][depths[depth]], true + for { + if c.pegRule != ruleUnknown && a.isParentOf(c) { + b = c + continue depthFirstSearch + } else if parent && b.end != a.end { + write(token32{pegRule: ruleSuf, begin: b.end, end: a.end}, true) + } + + depth-- + if depth > 0 { + a, b, c = ordered[depth-1][depths[depth-1]-1], a, ordered[depth][depths[depth]] + parent = a.isParentOf(b) + continue + } + + break depthFirstSearch + } + } + + close(s) + }() + return s, ordered +} + +func (t *tokens32) PrintSyntax() { + tokens, ordered := t.PreOrder() + max := -1 + for token := range tokens { + if !token.leaf { + fmt.Printf("%v", token.begin) + for i, leaf, depths := 0, int(token.next), token.depths; i < leaf; i++ { + fmt.Printf(" \x1B[36m%v\x1B[m", rul3s[ordered[i][depths[i]-1].pegRule]) + } + fmt.Printf(" \x1B[36m%v\x1B[m\n", rul3s[token.pegRule]) + } else if token.begin == token.end { + fmt.Printf("%v", token.begin) + for i, leaf, depths := 0, int(token.next), token.depths; i < leaf; i++ { + fmt.Printf(" \x1B[31m%v\x1B[m", rul3s[ordered[i][depths[i]-1].pegRule]) + } + fmt.Printf(" \x1B[31m%v\x1B[m\n", rul3s[token.pegRule]) + } else { + for c, end := token.begin, token.end; c < end; c++ { + if i := int(c); max+1 < i { + for j := max; j < i; j++ { + fmt.Printf("skip %v %v\n", j, token.String()) + } + max = i + } else if i := int(c); i <= max { + for j := i; j <= max; j++ { + fmt.Printf("dupe %v %v\n", j, token.String()) + } + } else { + max = int(c) + } + fmt.Printf("%v", c) + for i, leaf, depths := 0, int(token.next), token.depths; i < leaf; i++ { + fmt.Printf(" \x1B[34m%v\x1B[m", rul3s[ordered[i][depths[i]-1].pegRule]) + } + fmt.Printf(" \x1B[34m%v\x1B[m\n", rul3s[token.pegRule]) + } + fmt.Printf("\n") + } } - return nil } func (t *tokens32) PrintSyntaxTree(buffer string) { - t.AST().Print(buffer) + tokens, _ := t.PreOrder() + for token := range tokens { + for c := 0; c < int(token.next); c++ { + fmt.Printf(" ") + } + fmt.Printf("\x1B[34m%v\x1B[m %v\n", rul3s[token.pegRule], strconv.Quote(string(([]rune(buffer)[token.begin:token.end])))) + } } -func (t *tokens32) PrettyPrintSyntaxTree(buffer string) { - t.AST().PrettyPrint(buffer) +func (t *tokens32) Add(rule pegRule, begin, end, depth uint32, index int) { + t.tree[index] = token32{pegRule: rule, begin: uint32(begin), end: uint32(end), next: uint32(depth)} } -func (t *tokens32) Add(rule pegRule, begin, end, index uint32) { - if tree := t.tree; int(index) >= len(tree) { +func (t *tokens32) Tokens() <-chan token32 { + s := make(chan token32, 16) + go func() { + for _, v := range t.tree { + s <- v.getToken32() + } + close(s) + }() + return s +} + +func (t *tokens32) Error() []token32 { + ordered := t.Order() + length := len(ordered) + tokens, length := make([]token32, length), length-1 + for i := range tokens { + o := ordered[length-i] + if len(o) > 1 { + tokens[i] = o[len(o)-2].getToken32() + } + } + return tokens +} + +func (t *tokens32) Expand(index int) { + tree := t.tree + if index >= len(tree) { expanded := make([]token32, 2*len(tree)) copy(expanded, tree) t.tree = expanded } - t.tree[index] = token32{ - pegRule: rule, - begin: begin, - end: end, - } -} - -func (t *tokens32) Tokens() []token32 { - return t.tree } type QueryParser struct { Buffer string buffer []rune - rules [20]func() bool - parse func(rule ...int) error - reset func() + rules [21]func() bool + Parse func(rule ...int) error + Reset func() Pretty bool tokens32 } -func (p *QueryParser) Parse(rule ...int) error { - return p.parse(rule...) -} - -func (p *QueryParser) Reset() { - p.reset() -} - type textPosition struct { line, symbol int } @@ -249,34 +424,24 @@ func (e *parseError) Error() string { } func (p *QueryParser) PrintSyntaxTree() { - if p.Pretty { - p.tokens32.PrettyPrintSyntaxTree(p.Buffer) - } else { - p.tokens32.PrintSyntaxTree(p.Buffer) - } + p.tokens32.PrintSyntaxTree(p.Buffer) +} + +func (p *QueryParser) Highlighter() { + p.PrintSyntax() } func (p *QueryParser) Init() { - var ( - max token32 - position, tokenIndex uint32 - buffer []rune - ) - p.reset = func() { - max = token32{} - position, tokenIndex = 0, 0 - - p.buffer = []rune(p.Buffer) - if len(p.buffer) == 0 || p.buffer[len(p.buffer)-1] != endSymbol { - p.buffer = append(p.buffer, endSymbol) - } - buffer = p.buffer + p.buffer = []rune(p.Buffer) + if len(p.buffer) == 0 || p.buffer[len(p.buffer)-1] != endSymbol { + p.buffer = append(p.buffer, endSymbol) } - p.reset() - _rules := p.rules tree := tokens32{tree: make([]token32, math.MaxInt16)} - p.parse = func(rule ...int) error { + var max token32 + position, depth, tokenIndex, buffer, _rules := uint32(0), uint32(0), 0, p.buffer, p.rules + + p.Parse = func(rule ...int) error { r := 1 if len(rule) > 0 { r = rule[0] @@ -284,17 +449,22 @@ func (p *QueryParser) Init() { matches := p.rules[r]() p.tokens32 = tree if matches { - p.Trim(tokenIndex) + p.trim(tokenIndex) return nil } return &parseError{p, max} } + p.Reset = func() { + position, tokenIndex, depth = 0, 0, 0 + } + add := func(rule pegRule, begin uint32) { - tree.Add(rule, begin, position, tokenIndex) + tree.Expand(tokenIndex) + tree.Add(rule, begin, position, depth, tokenIndex) tokenIndex++ if begin != position && position > max.end { - max = token32{rule, begin, position} + max = token32{rule, begin, position, depth} } } @@ -326,9 +496,10 @@ func (p *QueryParser) Init() { nil, /* 0 e <- <('"' condition (' '+ and ' '+ condition)* '"' !.)> */ func() bool { - position0, tokenIndex0 := position, tokenIndex + position0, tokenIndex0, depth0 := position, tokenIndex, depth { position1 := position + depth++ if buffer[position] != rune('"') { goto l0 } @@ -338,33 +509,34 @@ func (p *QueryParser) Init() { } l2: { - position3, tokenIndex3 := position, tokenIndex + position3, tokenIndex3, depth3 := position, tokenIndex, depth if buffer[position] != rune(' ') { goto l3 } position++ l4: { - position5, tokenIndex5 := position, tokenIndex + position5, tokenIndex5, depth5 := position, tokenIndex, depth if buffer[position] != rune(' ') { goto l5 } position++ goto l4 l5: - position, tokenIndex = position5, tokenIndex5 + position, tokenIndex, depth = position5, tokenIndex5, depth5 } { position6 := position + depth++ { - position7, tokenIndex7 := position, tokenIndex + position7, tokenIndex7, depth7 := position, tokenIndex, depth if buffer[position] != rune('a') { goto l8 } position++ goto l7 l8: - position, tokenIndex = position7, tokenIndex7 + position, tokenIndex, depth = position7, tokenIndex7, depth7 if buffer[position] != rune('A') { goto l3 } @@ -372,14 +544,14 @@ func (p *QueryParser) Init() { } l7: { - position9, tokenIndex9 := position, tokenIndex + position9, tokenIndex9, depth9 := position, tokenIndex, depth if buffer[position] != rune('n') { goto l10 } position++ goto l9 l10: - position, tokenIndex = position9, tokenIndex9 + position, tokenIndex, depth = position9, tokenIndex9, depth9 if buffer[position] != rune('N') { goto l3 } @@ -387,20 +559,21 @@ func (p *QueryParser) Init() { } l9: { - position11, tokenIndex11 := position, tokenIndex + position11, tokenIndex11, depth11 := position, tokenIndex, depth if buffer[position] != rune('d') { goto l12 } position++ goto l11 l12: - position, tokenIndex = position11, tokenIndex11 + position, tokenIndex, depth = position11, tokenIndex11, depth11 if buffer[position] != rune('D') { goto l3 } position++ } l11: + depth-- add(ruleand, position6) } if buffer[position] != rune(' ') { @@ -409,53 +582,57 @@ func (p *QueryParser) Init() { position++ l13: { - position14, tokenIndex14 := position, tokenIndex + position14, tokenIndex14, depth14 := position, tokenIndex, depth if buffer[position] != rune(' ') { goto l14 } position++ goto l13 l14: - position, tokenIndex = position14, tokenIndex14 + position, tokenIndex, depth = position14, tokenIndex14, depth14 } if !_rules[rulecondition]() { goto l3 } goto l2 l3: - position, tokenIndex = position3, tokenIndex3 + position, tokenIndex, depth = position3, tokenIndex3, depth3 } if buffer[position] != rune('"') { goto l0 } position++ { - position15, tokenIndex15 := position, tokenIndex + position15, tokenIndex15, depth15 := position, tokenIndex, depth if !matchDot() { goto l15 } goto l0 l15: - position, tokenIndex = position15, tokenIndex15 + position, tokenIndex, depth = position15, tokenIndex15, depth15 } + depth-- add(rulee, position1) } return true l0: - position, tokenIndex = position0, tokenIndex0 + position, tokenIndex, depth = position0, tokenIndex0, depth0 return false }, - /* 1 condition <- <(tag ' '* ((le ' '* ((&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number))) / (ge ' '* ((&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number))) / ((&('=') (equal ' '* ((&('\'') value) | (&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number)))) | (&('>') (g ' '* ((&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number)))) | (&('<') (l ' '* ((&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number)))) | (&('C' | 'c') (contains ' '* value)))))> */ + /* 1 condition <- <(tag ' '* ((le ' '* ((&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number))) / (ge ' '* ((&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number))) / ((&('E' | 'e') exists) | (&('=') (equal ' '* ((&('\'') value) | (&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number)))) | (&('>') (g ' '* ((&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number)))) | (&('<') (l ' '* ((&('D' | 'd') date) | (&('T' | 't') time) | (&('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') number)))) | (&('C' | 'c') (contains ' '* value)))))> */ func() bool { - position16, tokenIndex16 := position, tokenIndex + position16, tokenIndex16, depth16 := position, tokenIndex, depth { position17 := position + depth++ { position18 := position + depth++ { position19 := position + depth++ { - position22, tokenIndex22 := position, tokenIndex + position22, tokenIndex22, depth22 := position, tokenIndex, depth { switch buffer[position] { case '<': @@ -535,16 +712,16 @@ func (p *QueryParser) Init() { goto l16 l22: - position, tokenIndex = position22, tokenIndex22 + position, tokenIndex, depth = position22, tokenIndex22, depth22 } if !matchDot() { goto l16 } l20: { - position21, tokenIndex21 := position, tokenIndex + position21, tokenIndex21, depth21 := position, tokenIndex, depth { - position24, tokenIndex24 := position, tokenIndex + position24, tokenIndex24, depth24 := position, tokenIndex, depth { switch buffer[position] { case '<': @@ -624,34 +801,37 @@ func (p *QueryParser) Init() { goto l21 l24: - position, tokenIndex = position24, tokenIndex24 + position, tokenIndex, depth = position24, tokenIndex24, depth24 } if !matchDot() { goto l21 } goto l20 l21: - position, tokenIndex = position21, tokenIndex21 + position, tokenIndex, depth = position21, tokenIndex21, depth21 } + depth-- add(rulePegText, position19) } + depth-- add(ruletag, position18) } l26: { - position27, tokenIndex27 := position, tokenIndex + position27, tokenIndex27, depth27 := position, tokenIndex, depth if buffer[position] != rune(' ') { goto l27 } position++ goto l26 l27: - position, tokenIndex = position27, tokenIndex27 + position, tokenIndex, depth = position27, tokenIndex27, depth27 } { - position28, tokenIndex28 := position, tokenIndex + position28, tokenIndex28, depth28 := position, tokenIndex, depth { position30 := position + depth++ if buffer[position] != rune('<') { goto l29 } @@ -660,18 +840,19 @@ func (p *QueryParser) Init() { goto l29 } position++ + depth-- add(rulele, position30) } l31: { - position32, tokenIndex32 := position, tokenIndex + position32, tokenIndex32, depth32 := position, tokenIndex, depth if buffer[position] != rune(' ') { goto l32 } position++ goto l31 l32: - position, tokenIndex = position32, tokenIndex32 + position, tokenIndex, depth = position32, tokenIndex32, depth32 } { switch buffer[position] { @@ -695,9 +876,10 @@ func (p *QueryParser) Init() { goto l28 l29: - position, tokenIndex = position28, tokenIndex28 + position, tokenIndex, depth = position28, tokenIndex28, depth28 { position35 := position + depth++ if buffer[position] != rune('>') { goto l34 } @@ -706,18 +888,19 @@ func (p *QueryParser) Init() { goto l34 } position++ + depth-- add(rulege, position35) } l36: { - position37, tokenIndex37 := position, tokenIndex + position37, tokenIndex37, depth37 := position, tokenIndex, depth if buffer[position] != rune(' ') { goto l37 } position++ goto l36 l37: - position, tokenIndex = position37, tokenIndex37 + position, tokenIndex, depth = position37, tokenIndex37, depth37 } { switch buffer[position] { @@ -741,28 +924,128 @@ func (p *QueryParser) Init() { goto l28 l34: - position, tokenIndex = position28, tokenIndex28 + position, tokenIndex, depth = position28, tokenIndex28, depth28 { switch buffer[position] { - case '=': + case 'E', 'e': { position40 := position + depth++ + { + position41, tokenIndex41, depth41 := position, tokenIndex, depth + if buffer[position] != rune('e') { + goto l42 + } + position++ + goto l41 + l42: + position, tokenIndex, depth = position41, tokenIndex41, depth41 + if buffer[position] != rune('E') { + goto l16 + } + position++ + } + l41: + { + position43, tokenIndex43, depth43 := position, tokenIndex, depth + if buffer[position] != rune('x') { + goto l44 + } + position++ + goto l43 + l44: + position, tokenIndex, depth = position43, tokenIndex43, depth43 + if buffer[position] != rune('X') { + goto l16 + } + position++ + } + l43: + { + position45, tokenIndex45, depth45 := position, tokenIndex, depth + if buffer[position] != rune('i') { + goto l46 + } + position++ + goto l45 + l46: + position, tokenIndex, depth = position45, tokenIndex45, depth45 + if buffer[position] != rune('I') { + goto l16 + } + position++ + } + l45: + { + position47, tokenIndex47, depth47 := position, tokenIndex, depth + if buffer[position] != rune('s') { + goto l48 + } + position++ + goto l47 + l48: + position, tokenIndex, depth = position47, tokenIndex47, depth47 + if buffer[position] != rune('S') { + goto l16 + } + position++ + } + l47: + { + position49, tokenIndex49, depth49 := position, tokenIndex, depth + if buffer[position] != rune('t') { + goto l50 + } + position++ + goto l49 + l50: + position, tokenIndex, depth = position49, tokenIndex49, depth49 + if buffer[position] != rune('T') { + goto l16 + } + position++ + } + l49: + { + position51, tokenIndex51, depth51 := position, tokenIndex, depth + if buffer[position] != rune('s') { + goto l52 + } + position++ + goto l51 + l52: + position, tokenIndex, depth = position51, tokenIndex51, depth51 + if buffer[position] != rune('S') { + goto l16 + } + position++ + } + l51: + depth-- + add(ruleexists, position40) + } + break + case '=': + { + position53 := position + depth++ if buffer[position] != rune('=') { goto l16 } position++ - add(ruleequal, position40) + depth-- + add(ruleequal, position53) } - l41: + l54: { - position42, tokenIndex42 := position, tokenIndex + position55, tokenIndex55, depth55 := position, tokenIndex, depth if buffer[position] != rune(' ') { - goto l42 + goto l55 } position++ - goto l41 - l42: - position, tokenIndex = position42, tokenIndex42 + goto l54 + l55: + position, tokenIndex, depth = position55, tokenIndex55, depth55 } { switch buffer[position] { @@ -792,23 +1075,25 @@ func (p *QueryParser) Init() { break case '>': { - position44 := position + position57 := position + depth++ if buffer[position] != rune('>') { goto l16 } position++ - add(ruleg, position44) + depth-- + add(ruleg, position57) } - l45: + l58: { - position46, tokenIndex46 := position, tokenIndex + position59, tokenIndex59, depth59 := position, tokenIndex, depth if buffer[position] != rune(' ') { - goto l46 + goto l59 } position++ - goto l45 - l46: - position, tokenIndex = position46, tokenIndex46 + goto l58 + l59: + position, tokenIndex, depth = position59, tokenIndex59, depth59 } { switch buffer[position] { @@ -833,23 +1118,25 @@ func (p *QueryParser) Init() { break case '<': { - position48 := position + position61 := position + depth++ if buffer[position] != rune('<') { goto l16 } position++ - add(rulel, position48) + depth-- + add(rulel, position61) } - l49: + l62: { - position50, tokenIndex50 := position, tokenIndex + position63, tokenIndex63, depth63 := position, tokenIndex, depth if buffer[position] != rune(' ') { - goto l50 + goto l63 } position++ - goto l49 - l50: - position, tokenIndex = position50, tokenIndex50 + goto l62 + l63: + position, tokenIndex, depth = position63, tokenIndex63, depth63 } { switch buffer[position] { @@ -874,139 +1161,141 @@ func (p *QueryParser) Init() { break default: { - position52 := position + position65 := position + depth++ { - position53, tokenIndex53 := position, tokenIndex + position66, tokenIndex66, depth66 := position, tokenIndex, depth if buffer[position] != rune('c') { - goto l54 + goto l67 } position++ - goto l53 - l54: - position, tokenIndex = position53, tokenIndex53 + goto l66 + l67: + position, tokenIndex, depth = position66, tokenIndex66, depth66 if buffer[position] != rune('C') { goto l16 } position++ } - l53: + l66: { - position55, tokenIndex55 := position, tokenIndex + position68, tokenIndex68, depth68 := position, tokenIndex, depth if buffer[position] != rune('o') { - goto l56 + goto l69 } position++ - goto l55 - l56: - position, tokenIndex = position55, tokenIndex55 + goto l68 + l69: + position, tokenIndex, depth = position68, tokenIndex68, depth68 if buffer[position] != rune('O') { goto l16 } position++ } - l55: + l68: { - position57, tokenIndex57 := position, tokenIndex + position70, tokenIndex70, depth70 := position, tokenIndex, depth if buffer[position] != rune('n') { - goto l58 + goto l71 } position++ - goto l57 - l58: - position, tokenIndex = position57, tokenIndex57 + goto l70 + l71: + position, tokenIndex, depth = position70, tokenIndex70, depth70 if buffer[position] != rune('N') { goto l16 } position++ } - l57: + l70: { - position59, tokenIndex59 := position, tokenIndex + position72, tokenIndex72, depth72 := position, tokenIndex, depth if buffer[position] != rune('t') { - goto l60 + goto l73 } position++ - goto l59 - l60: - position, tokenIndex = position59, tokenIndex59 + goto l72 + l73: + position, tokenIndex, depth = position72, tokenIndex72, depth72 if buffer[position] != rune('T') { goto l16 } position++ } - l59: + l72: { - position61, tokenIndex61 := position, tokenIndex + position74, tokenIndex74, depth74 := position, tokenIndex, depth if buffer[position] != rune('a') { - goto l62 + goto l75 } position++ - goto l61 - l62: - position, tokenIndex = position61, tokenIndex61 + goto l74 + l75: + position, tokenIndex, depth = position74, tokenIndex74, depth74 if buffer[position] != rune('A') { goto l16 } position++ } - l61: + l74: { - position63, tokenIndex63 := position, tokenIndex + position76, tokenIndex76, depth76 := position, tokenIndex, depth if buffer[position] != rune('i') { - goto l64 + goto l77 } position++ - goto l63 - l64: - position, tokenIndex = position63, tokenIndex63 + goto l76 + l77: + position, tokenIndex, depth = position76, tokenIndex76, depth76 if buffer[position] != rune('I') { goto l16 } position++ } - l63: + l76: { - position65, tokenIndex65 := position, tokenIndex + position78, tokenIndex78, depth78 := position, tokenIndex, depth if buffer[position] != rune('n') { - goto l66 + goto l79 } position++ - goto l65 - l66: - position, tokenIndex = position65, tokenIndex65 + goto l78 + l79: + position, tokenIndex, depth = position78, tokenIndex78, depth78 if buffer[position] != rune('N') { goto l16 } position++ } - l65: + l78: { - position67, tokenIndex67 := position, tokenIndex + position80, tokenIndex80, depth80 := position, tokenIndex, depth if buffer[position] != rune('s') { - goto l68 + goto l81 } position++ - goto l67 - l68: - position, tokenIndex = position67, tokenIndex67 + goto l80 + l81: + position, tokenIndex, depth = position80, tokenIndex80, depth80 if buffer[position] != rune('S') { goto l16 } position++ } - l67: - add(rulecontains, position52) + l80: + depth-- + add(rulecontains, position65) } - l69: + l82: { - position70, tokenIndex70 := position, tokenIndex + position83, tokenIndex83, depth83 := position, tokenIndex, depth if buffer[position] != rune(' ') { - goto l70 + goto l83 } position++ - goto l69 - l70: - position, tokenIndex = position70, tokenIndex70 + goto l82 + l83: + position, tokenIndex, depth = position83, tokenIndex83, depth83 } if !_rules[rulevalue]() { goto l16 @@ -1017,508 +1306,532 @@ func (p *QueryParser) Init() { } l28: + depth-- add(rulecondition, position17) } return true l16: - position, tokenIndex = position16, tokenIndex16 + position, tokenIndex, depth = position16, tokenIndex16, depth16 return false }, /* 2 tag <- <<(!((&('<') '<') | (&('>') '>') | (&('=') '=') | (&('\'') '\'') | (&('"') '"') | (&(')') ')') | (&('(') '(') | (&('\\') '\\') | (&('\r') '\r') | (&('\n') '\n') | (&('\t') '\t') | (&(' ') ' ')) .)+>> */ nil, /* 3 value <- <<('\'' (!('"' / '\'') .)* '\'')>> */ func() bool { - position72, tokenIndex72 := position, tokenIndex + position85, tokenIndex85, depth85 := position, tokenIndex, depth { - position73 := position + position86 := position + depth++ { - position74 := position + position87 := position + depth++ if buffer[position] != rune('\'') { - goto l72 + goto l85 } position++ - l75: + l88: { - position76, tokenIndex76 := position, tokenIndex + position89, tokenIndex89, depth89 := position, tokenIndex, depth { - position77, tokenIndex77 := position, tokenIndex + position90, tokenIndex90, depth90 := position, tokenIndex, depth { - position78, tokenIndex78 := position, tokenIndex + position91, tokenIndex91, depth91 := position, tokenIndex, depth if buffer[position] != rune('"') { - goto l79 + goto l92 } position++ - goto l78 - l79: - position, tokenIndex = position78, tokenIndex78 + goto l91 + l92: + position, tokenIndex, depth = position91, tokenIndex91, depth91 if buffer[position] != rune('\'') { - goto l77 + goto l90 } position++ } - l78: - goto l76 - l77: - position, tokenIndex = position77, tokenIndex77 + l91: + goto l89 + l90: + position, tokenIndex, depth = position90, tokenIndex90, depth90 } if !matchDot() { - goto l76 + goto l89 } - goto l75 - l76: - position, tokenIndex = position76, tokenIndex76 + goto l88 + l89: + position, tokenIndex, depth = position89, tokenIndex89, depth89 } if buffer[position] != rune('\'') { - goto l72 + goto l85 } position++ - add(rulePegText, position74) + depth-- + add(rulePegText, position87) } - add(rulevalue, position73) + depth-- + add(rulevalue, position86) } return true - l72: - position, tokenIndex = position72, tokenIndex72 + l85: + position, tokenIndex, depth = position85, tokenIndex85, depth85 return false }, /* 4 number <- <<('0' / ([1-9] digit* ('.' digit*)?))>> */ func() bool { - position80, tokenIndex80 := position, tokenIndex + position93, tokenIndex93, depth93 := position, tokenIndex, depth { - position81 := position + position94 := position + depth++ { - position82 := position + position95 := position + depth++ { - position83, tokenIndex83 := position, tokenIndex + position96, tokenIndex96, depth96 := position, tokenIndex, depth if buffer[position] != rune('0') { - goto l84 + goto l97 } position++ - goto l83 - l84: - position, tokenIndex = position83, tokenIndex83 + goto l96 + l97: + position, tokenIndex, depth = position96, tokenIndex96, depth96 if c := buffer[position]; c < rune('1') || c > rune('9') { - goto l80 + goto l93 } position++ - l85: + l98: { - position86, tokenIndex86 := position, tokenIndex + position99, tokenIndex99, depth99 := position, tokenIndex, depth if !_rules[ruledigit]() { - goto l86 + goto l99 } - goto l85 - l86: - position, tokenIndex = position86, tokenIndex86 + goto l98 + l99: + position, tokenIndex, depth = position99, tokenIndex99, depth99 } { - position87, tokenIndex87 := position, tokenIndex + position100, tokenIndex100, depth100 := position, tokenIndex, depth if buffer[position] != rune('.') { - goto l87 + goto l100 } position++ - l89: + l102: { - position90, tokenIndex90 := position, tokenIndex + position103, tokenIndex103, depth103 := position, tokenIndex, depth if !_rules[ruledigit]() { - goto l90 + goto l103 } - goto l89 - l90: - position, tokenIndex = position90, tokenIndex90 + goto l102 + l103: + position, tokenIndex, depth = position103, tokenIndex103, depth103 } - goto l88 - l87: - position, tokenIndex = position87, tokenIndex87 + goto l101 + l100: + position, tokenIndex, depth = position100, tokenIndex100, depth100 } - l88: + l101: } - l83: - add(rulePegText, position82) + l96: + depth-- + add(rulePegText, position95) } - add(rulenumber, position81) + depth-- + add(rulenumber, position94) } return true - l80: - position, tokenIndex = position80, tokenIndex80 + l93: + position, tokenIndex, depth = position93, tokenIndex93, depth93 return false }, /* 5 digit <- <[0-9]> */ func() bool { - position91, tokenIndex91 := position, tokenIndex + position104, tokenIndex104, depth104 := position, tokenIndex, depth { - position92 := position + position105 := position + depth++ if c := buffer[position]; c < rune('0') || c > rune('9') { - goto l91 + goto l104 } position++ - add(ruledigit, position92) + depth-- + add(ruledigit, position105) } return true - l91: - position, tokenIndex = position91, tokenIndex91 + l104: + position, tokenIndex, depth = position104, tokenIndex104, depth104 return false }, /* 6 time <- <(('t' / 'T') ('i' / 'I') ('m' / 'M') ('e' / 'E') ' ' <(year '-' month '-' day 'T' digit digit ':' digit digit ':' digit digit ((('-' / '+') digit digit ':' digit digit) / 'Z'))>)> */ func() bool { - position93, tokenIndex93 := position, tokenIndex + position106, tokenIndex106, depth106 := position, tokenIndex, depth { - position94 := position + position107 := position + depth++ { - position95, tokenIndex95 := position, tokenIndex + position108, tokenIndex108, depth108 := position, tokenIndex, depth if buffer[position] != rune('t') { - goto l96 + goto l109 } position++ - goto l95 - l96: - position, tokenIndex = position95, tokenIndex95 + goto l108 + l109: + position, tokenIndex, depth = position108, tokenIndex108, depth108 if buffer[position] != rune('T') { - goto l93 + goto l106 } position++ } - l95: + l108: { - position97, tokenIndex97 := position, tokenIndex + position110, tokenIndex110, depth110 := position, tokenIndex, depth if buffer[position] != rune('i') { - goto l98 - } - position++ - goto l97 - l98: - position, tokenIndex = position97, tokenIndex97 - if buffer[position] != rune('I') { - goto l93 - } - position++ - } - l97: - { - position99, tokenIndex99 := position, tokenIndex - if buffer[position] != rune('m') { - goto l100 - } - position++ - goto l99 - l100: - position, tokenIndex = position99, tokenIndex99 - if buffer[position] != rune('M') { - goto l93 - } - position++ - } - l99: - { - position101, tokenIndex101 := position, tokenIndex - if buffer[position] != rune('e') { - goto l102 - } - position++ - goto l101 - l102: - position, tokenIndex = position101, tokenIndex101 - if buffer[position] != rune('E') { - goto l93 - } - position++ - } - l101: - if buffer[position] != rune(' ') { - goto l93 - } - position++ - { - position103 := position - if !_rules[ruleyear]() { - goto l93 - } - if buffer[position] != rune('-') { - goto l93 - } - position++ - if !_rules[rulemonth]() { - goto l93 - } - if buffer[position] != rune('-') { - goto l93 - } - position++ - if !_rules[ruleday]() { - goto l93 - } - if buffer[position] != rune('T') { - goto l93 - } - position++ - if !_rules[ruledigit]() { - goto l93 - } - if !_rules[ruledigit]() { - goto l93 - } - if buffer[position] != rune(':') { - goto l93 - } - position++ - if !_rules[ruledigit]() { - goto l93 - } - if !_rules[ruledigit]() { - goto l93 - } - if buffer[position] != rune(':') { - goto l93 - } - position++ - if !_rules[ruledigit]() { - goto l93 - } - if !_rules[ruledigit]() { - goto l93 - } - { - position104, tokenIndex104 := position, tokenIndex - { - position106, tokenIndex106 := position, tokenIndex - if buffer[position] != rune('-') { - goto l107 - } - position++ - goto l106 - l107: - position, tokenIndex = position106, tokenIndex106 - if buffer[position] != rune('+') { - goto l105 - } - position++ - } - l106: - if !_rules[ruledigit]() { - goto l105 - } - if !_rules[ruledigit]() { - goto l105 - } - if buffer[position] != rune(':') { - goto l105 - } - position++ - if !_rules[ruledigit]() { - goto l105 - } - if !_rules[ruledigit]() { - goto l105 - } - goto l104 - l105: - position, tokenIndex = position104, tokenIndex104 - if buffer[position] != rune('Z') { - goto l93 - } - position++ - } - l104: - add(rulePegText, position103) - } - add(ruletime, position94) - } - return true - l93: - position, tokenIndex = position93, tokenIndex93 - return false - }, - /* 7 date <- <(('d' / 'D') ('a' / 'A') ('t' / 'T') ('e' / 'E') ' ' <(year '-' month '-' day)>)> */ - func() bool { - position108, tokenIndex108 := position, tokenIndex - { - position109 := position - { - position110, tokenIndex110 := position, tokenIndex - if buffer[position] != rune('d') { goto l111 } position++ goto l110 l111: - position, tokenIndex = position110, tokenIndex110 - if buffer[position] != rune('D') { - goto l108 + position, tokenIndex, depth = position110, tokenIndex110, depth110 + if buffer[position] != rune('I') { + goto l106 } position++ } l110: { - position112, tokenIndex112 := position, tokenIndex - if buffer[position] != rune('a') { + position112, tokenIndex112, depth112 := position, tokenIndex, depth + if buffer[position] != rune('m') { goto l113 } position++ goto l112 l113: - position, tokenIndex = position112, tokenIndex112 - if buffer[position] != rune('A') { - goto l108 + position, tokenIndex, depth = position112, tokenIndex112, depth112 + if buffer[position] != rune('M') { + goto l106 } position++ } l112: { - position114, tokenIndex114 := position, tokenIndex - if buffer[position] != rune('t') { + position114, tokenIndex114, depth114 := position, tokenIndex, depth + if buffer[position] != rune('e') { goto l115 } position++ goto l114 l115: - position, tokenIndex = position114, tokenIndex114 - if buffer[position] != rune('T') { - goto l108 + position, tokenIndex, depth = position114, tokenIndex114, depth114 + if buffer[position] != rune('E') { + goto l106 } position++ } l114: - { - position116, tokenIndex116 := position, tokenIndex - if buffer[position] != rune('e') { - goto l117 - } - position++ - goto l116 - l117: - position, tokenIndex = position116, tokenIndex116 - if buffer[position] != rune('E') { - goto l108 - } - position++ - } - l116: if buffer[position] != rune(' ') { - goto l108 + goto l106 } position++ { - position118 := position + position116 := position + depth++ if !_rules[ruleyear]() { - goto l108 + goto l106 } if buffer[position] != rune('-') { - goto l108 + goto l106 } position++ if !_rules[rulemonth]() { - goto l108 + goto l106 } if buffer[position] != rune('-') { - goto l108 + goto l106 } position++ if !_rules[ruleday]() { - goto l108 + goto l106 } - add(rulePegText, position118) - } - add(ruledate, position109) - } - return true - l108: - position, tokenIndex = position108, tokenIndex108 - return false - }, - /* 8 year <- <(('1' / '2') digit digit digit)> */ - func() bool { - position119, tokenIndex119 := position, tokenIndex - { - position120 := position - { - position121, tokenIndex121 := position, tokenIndex - if buffer[position] != rune('1') { - goto l122 + if buffer[position] != rune('T') { + goto l106 } position++ - goto l121 - l122: - position, tokenIndex = position121, tokenIndex121 - if buffer[position] != rune('2') { - goto l119 + if !_rules[ruledigit]() { + goto l106 + } + if !_rules[ruledigit]() { + goto l106 + } + if buffer[position] != rune(':') { + goto l106 + } + position++ + if !_rules[ruledigit]() { + goto l106 + } + if !_rules[ruledigit]() { + goto l106 + } + if buffer[position] != rune(':') { + goto l106 + } + position++ + if !_rules[ruledigit]() { + goto l106 + } + if !_rules[ruledigit]() { + goto l106 + } + { + position117, tokenIndex117, depth117 := position, tokenIndex, depth + { + position119, tokenIndex119, depth119 := position, tokenIndex, depth + if buffer[position] != rune('-') { + goto l120 + } + position++ + goto l119 + l120: + position, tokenIndex, depth = position119, tokenIndex119, depth119 + if buffer[position] != rune('+') { + goto l118 + } + position++ + } + l119: + if !_rules[ruledigit]() { + goto l118 + } + if !_rules[ruledigit]() { + goto l118 + } + if buffer[position] != rune(':') { + goto l118 + } + position++ + if !_rules[ruledigit]() { + goto l118 + } + if !_rules[ruledigit]() { + goto l118 + } + goto l117 + l118: + position, tokenIndex, depth = position117, tokenIndex117, depth117 + if buffer[position] != rune('Z') { + goto l106 + } + position++ + } + l117: + depth-- + add(rulePegText, position116) + } + depth-- + add(ruletime, position107) + } + return true + l106: + position, tokenIndex, depth = position106, tokenIndex106, depth106 + return false + }, + /* 7 date <- <(('d' / 'D') ('a' / 'A') ('t' / 'T') ('e' / 'E') ' ' <(year '-' month '-' day)>)> */ + func() bool { + position121, tokenIndex121, depth121 := position, tokenIndex, depth + { + position122 := position + depth++ + { + position123, tokenIndex123, depth123 := position, tokenIndex, depth + if buffer[position] != rune('d') { + goto l124 + } + position++ + goto l123 + l124: + position, tokenIndex, depth = position123, tokenIndex123, depth123 + if buffer[position] != rune('D') { + goto l121 } position++ } - l121: - if !_rules[ruledigit]() { - goto l119 - } - if !_rules[ruledigit]() { - goto l119 - } - if !_rules[ruledigit]() { - goto l119 - } - add(ruleyear, position120) - } - return true - l119: - position, tokenIndex = position119, tokenIndex119 - return false - }, - /* 9 month <- <(('0' / '1') digit)> */ - func() bool { - position123, tokenIndex123 := position, tokenIndex - { - position124 := position + l123: { - position125, tokenIndex125 := position, tokenIndex - if buffer[position] != rune('0') { + position125, tokenIndex125, depth125 := position, tokenIndex, depth + if buffer[position] != rune('a') { goto l126 } position++ goto l125 l126: - position, tokenIndex = position125, tokenIndex125 - if buffer[position] != rune('1') { - goto l123 + position, tokenIndex, depth = position125, tokenIndex125, depth125 + if buffer[position] != rune('A') { + goto l121 } position++ } l125: - if !_rules[ruledigit]() { - goto l123 + { + position127, tokenIndex127, depth127 := position, tokenIndex, depth + if buffer[position] != rune('t') { + goto l128 + } + position++ + goto l127 + l128: + position, tokenIndex, depth = position127, tokenIndex127, depth127 + if buffer[position] != rune('T') { + goto l121 + } + position++ } - add(rulemonth, position124) + l127: + { + position129, tokenIndex129, depth129 := position, tokenIndex, depth + if buffer[position] != rune('e') { + goto l130 + } + position++ + goto l129 + l130: + position, tokenIndex, depth = position129, tokenIndex129, depth129 + if buffer[position] != rune('E') { + goto l121 + } + position++ + } + l129: + if buffer[position] != rune(' ') { + goto l121 + } + position++ + { + position131 := position + depth++ + if !_rules[ruleyear]() { + goto l121 + } + if buffer[position] != rune('-') { + goto l121 + } + position++ + if !_rules[rulemonth]() { + goto l121 + } + if buffer[position] != rune('-') { + goto l121 + } + position++ + if !_rules[ruleday]() { + goto l121 + } + depth-- + add(rulePegText, position131) + } + depth-- + add(ruledate, position122) } return true - l123: - position, tokenIndex = position123, tokenIndex123 + l121: + position, tokenIndex, depth = position121, tokenIndex121, depth121 + return false + }, + /* 8 year <- <(('1' / '2') digit digit digit)> */ + func() bool { + position132, tokenIndex132, depth132 := position, tokenIndex, depth + { + position133 := position + depth++ + { + position134, tokenIndex134, depth134 := position, tokenIndex, depth + if buffer[position] != rune('1') { + goto l135 + } + position++ + goto l134 + l135: + position, tokenIndex, depth = position134, tokenIndex134, depth134 + if buffer[position] != rune('2') { + goto l132 + } + position++ + } + l134: + if !_rules[ruledigit]() { + goto l132 + } + if !_rules[ruledigit]() { + goto l132 + } + if !_rules[ruledigit]() { + goto l132 + } + depth-- + add(ruleyear, position133) + } + return true + l132: + position, tokenIndex, depth = position132, tokenIndex132, depth132 + return false + }, + /* 9 month <- <(('0' / '1') digit)> */ + func() bool { + position136, tokenIndex136, depth136 := position, tokenIndex, depth + { + position137 := position + depth++ + { + position138, tokenIndex138, depth138 := position, tokenIndex, depth + if buffer[position] != rune('0') { + goto l139 + } + position++ + goto l138 + l139: + position, tokenIndex, depth = position138, tokenIndex138, depth138 + if buffer[position] != rune('1') { + goto l136 + } + position++ + } + l138: + if !_rules[ruledigit]() { + goto l136 + } + depth-- + add(rulemonth, position137) + } + return true + l136: + position, tokenIndex, depth = position136, tokenIndex136, depth136 return false }, /* 10 day <- <(((&('3') '3') | (&('2') '2') | (&('1') '1') | (&('0') '0')) digit)> */ func() bool { - position127, tokenIndex127 := position, tokenIndex + position140, tokenIndex140, depth140 := position, tokenIndex, depth { - position128 := position + position141 := position + depth++ { switch buffer[position] { case '3': if buffer[position] != rune('3') { - goto l127 + goto l140 } position++ break case '2': if buffer[position] != rune('2') { - goto l127 + goto l140 } position++ break case '1': if buffer[position] != rune('1') { - goto l127 + goto l140 } position++ break default: if buffer[position] != rune('0') { - goto l127 + goto l140 } position++ break @@ -1526,13 +1839,14 @@ func (p *QueryParser) Init() { } if !_rules[ruledigit]() { - goto l127 + goto l140 } - add(ruleday, position128) + depth-- + add(ruleday, position141) } return true - l127: - position, tokenIndex = position127, tokenIndex127 + l140: + position, tokenIndex, depth = position140, tokenIndex140, depth140 return false }, /* 11 and <- <(('a' / 'A') ('n' / 'N') ('d' / 'D'))> */ @@ -1541,13 +1855,15 @@ func (p *QueryParser) Init() { nil, /* 13 contains <- <(('c' / 'C') ('o' / 'O') ('n' / 'N') ('t' / 'T') ('a' / 'A') ('i' / 'I') ('n' / 'N') ('s' / 'S'))> */ nil, - /* 14 le <- <('<' '=')> */ + /* 14 exists <- <(('e' / 'E') ('x' / 'X') ('i' / 'I') ('s' / 'S') ('t' / 'T') ('s' / 'S'))> */ nil, - /* 15 ge <- <('>' '=')> */ + /* 15 le <- <('<' '=')> */ nil, - /* 16 l <- <'<'> */ + /* 16 ge <- <('>' '=')> */ nil, - /* 17 g <- <'>'> */ + /* 17 l <- <'<'> */ + nil, + /* 18 g <- <'>'> */ nil, nil, } diff --git a/libs/pubsub/query/query_test.go b/libs/pubsub/query/query_test.go index 0572c74ff..07586eab8 100644 --- a/libs/pubsub/query/query_test.go +++ b/libs/pubsub/query/query_test.go @@ -112,6 +112,44 @@ func TestMatches(t *testing.T) { false, false, }, + {"slash EXISTS", + map[string][]string{"slash.reason": {"missing_signature"}, "slash.power": {"6000"}}, + false, + true, + false, + }, + {"sl EXISTS", + map[string][]string{"slash.reason": {"missing_signature"}, "slash.power": {"6000"}}, + false, + true, + false, + }, + {"slash EXISTS", + map[string][]string{"transfer.recipient": {"cosmos1gu6y2a0ffteesyeyeesk23082c6998xyzmt9mz"}, + "transfer.sender": {"cosmos1crje20aj4gxdtyct7z3knxqry2jqt2fuaey6u5"}}, + false, + false, + false, + }, + {"slash.reason EXISTS AND slash.power > 1000", + map[string][]string{"slash.reason": {"missing_signature"}, "slash.power": {"6000"}}, + false, + true, + false, + }, + {"slash.reason EXISTS AND slash.power > 1000", + map[string][]string{"slash.reason": {"missing_signature"}, "slash.power": {"500"}}, + false, + false, + false, + }, + {"slash.reason EXISTS", + map[string][]string{"transfer.recipient": {"cosmos1gu6y2a0ffteesyeyeesk23082c6998xyzmt9mz"}, + "transfer.sender": {"cosmos1crje20aj4gxdtyct7z3knxqry2jqt2fuaey6u5"}}, + false, + false, + false, + }, } for _, tc := range testCases { @@ -119,7 +157,6 @@ func TestMatches(t *testing.T) { if !tc.err { require.Nil(t, err) } - require.NotNil(t, q, "Query '%s' should not be nil", tc.s) if tc.matches { @@ -166,6 +203,12 @@ func TestConditions(t *testing.T) { {Tag: "tx.time", Op: query.OpGreaterEqual, Operand: txTime}, }, }, + { + s: "slashing EXISTS", + conditions: []query.Condition{ + {Tag: "slashing", Op: query.OpExists}, + }, + }, } for _, tc := range testCases { diff --git a/rpc/swagger/swagger.yaml b/rpc/swagger/swagger.yaml index fa7825aae..2ec7f1b40 100644 --- a/rpc/swagger/swagger.yaml +++ b/rpc/swagger/swagger.yaml @@ -156,8 +156,8 @@ paths: string, which has a form: "condition AND condition ..." (no OR at the moment). condition has a form: "key operation operand". key is a string with a restricted set of possible symbols ( \t\n\r\\()"'=>< are not allowed). - operation can be "=", "<", "<=", ">", ">=", "CONTAINS". operand can be a - string (escaped with single quotes), number, date or time. + operation can be "=", "<", "<=", ">", ">=", "CONTAINS" AND "EXISTS". operand + can be a string (escaped with single quotes), number, date or time. Examples: tm.event = 'NewBlock' # new blocks