github.com/number571/tendermint@v0.34.11-gost/libs/pubsub/query/query.go (about) 1 // Package query provides a parser for a custom query format: 2 // 3 // abci.invoice.number=22 AND abci.invoice.owner=Ivan 4 // 5 // See query.peg for the grammar, which is a https://en.wikipedia.org/wiki/Parsing_expression_grammar. 6 // More: https://github.com/PhilippeSigaud/Pegged/wiki/PEG-Basics 7 // 8 // It has a support for numbers (integer and floating point), dates and times. 9 package query 10 11 import ( 12 "fmt" 13 "reflect" 14 "regexp" 15 "strconv" 16 "strings" 17 "time" 18 19 "github.com/number571/tendermint/abci/types" 20 ) 21 22 var ( 23 numRegex = regexp.MustCompile(`([0-9\.]+)`) 24 ) 25 26 // Query holds the query string and the query parser. 27 type Query struct { 28 str string 29 parser *QueryParser 30 } 31 32 // Condition represents a single condition within a query and consists of composite key 33 // (e.g. "tx.gas"), operator (e.g. "=") and operand (e.g. "7"). 34 type Condition struct { 35 CompositeKey string 36 Op Operator 37 Operand interface{} 38 } 39 40 // New parses the given string and returns a query or error if the string is 41 // invalid. 42 func New(s string) (*Query, error) { 43 p := &QueryParser{Buffer: fmt.Sprintf(`"%s"`, s)} 44 p.Init() 45 if err := p.Parse(); err != nil { 46 return nil, err 47 } 48 return &Query{str: s, parser: p}, nil 49 } 50 51 // MustParse turns the given string into a query or panics; for tests or others 52 // cases where you know the string is valid. 53 func MustParse(s string) *Query { 54 q, err := New(s) 55 if err != nil { 56 panic(fmt.Sprintf("failed to parse %s: %v", s, err)) 57 } 58 return q 59 } 60 61 // String returns the original string. 62 func (q *Query) String() string { 63 return q.str 64 } 65 66 // Operator is an operator that defines some kind of relation between composite key and 67 // operand (equality, etc.). 68 type Operator uint8 69 70 const ( 71 // "<=" 72 OpLessEqual Operator = iota 73 // ">=" 74 OpGreaterEqual 75 // "<" 76 OpLess 77 // ">" 78 OpGreater 79 // "=" 80 OpEqual 81 // "CONTAINS"; used to check if a string contains a certain sub string. 82 OpContains 83 // "EXISTS"; used to check if a certain event attribute is present. 84 OpExists 85 ) 86 87 const ( 88 // DateLayout defines a layout for all dates (`DATE date`) 89 DateLayout = "2006-01-02" 90 // TimeLayout defines a layout for all times (`TIME time`) 91 TimeLayout = time.RFC3339 92 ) 93 94 // Conditions returns a list of conditions. It returns an error if there is any 95 // error with the provided grammar in the Query. 96 func (q *Query) Conditions() ([]Condition, error) { 97 var ( 98 eventAttr string 99 op Operator 100 ) 101 102 conditions := make([]Condition, 0) 103 buffer, begin, end := q.parser.Buffer, 0, 0 104 105 // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7") 106 for token := range q.parser.Tokens() { 107 switch token.pegRule { 108 case rulePegText: 109 begin, end = int(token.begin), int(token.end) 110 111 case ruletag: 112 eventAttr = buffer[begin:end] 113 114 case rulele: 115 op = OpLessEqual 116 117 case rulege: 118 op = OpGreaterEqual 119 120 case rulel: 121 op = OpLess 122 123 case ruleg: 124 op = OpGreater 125 126 case ruleequal: 127 op = OpEqual 128 129 case rulecontains: 130 op = OpContains 131 132 case ruleexists: 133 op = OpExists 134 conditions = append(conditions, Condition{eventAttr, op, nil}) 135 136 case rulevalue: 137 // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock") 138 valueWithoutSingleQuotes := buffer[begin+1 : end-1] 139 conditions = append(conditions, Condition{eventAttr, op, valueWithoutSingleQuotes}) 140 141 case rulenumber: 142 number := buffer[begin:end] 143 if strings.ContainsAny(number, ".") { // if it looks like a floating-point number 144 value, err := strconv.ParseFloat(number, 64) 145 if err != nil { 146 err = fmt.Errorf( 147 "got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", 148 err, number, 149 ) 150 return nil, err 151 } 152 153 conditions = append(conditions, Condition{eventAttr, op, value}) 154 } else { 155 value, err := strconv.ParseInt(number, 10, 64) 156 if err != nil { 157 err = fmt.Errorf( 158 "got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", 159 err, number, 160 ) 161 return nil, err 162 } 163 164 conditions = append(conditions, Condition{eventAttr, op, value}) 165 } 166 167 case ruletime: 168 value, err := time.Parse(TimeLayout, buffer[begin:end]) 169 if err != nil { 170 err = fmt.Errorf( 171 "got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", 172 err, buffer[begin:end], 173 ) 174 return nil, err 175 } 176 177 conditions = append(conditions, Condition{eventAttr, op, value}) 178 179 case ruledate: 180 value, err := time.Parse("2006-01-02", buffer[begin:end]) 181 if err != nil { 182 err = fmt.Errorf( 183 "got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", 184 err, buffer[begin:end], 185 ) 186 return nil, err 187 } 188 189 conditions = append(conditions, Condition{eventAttr, op, value}) 190 } 191 } 192 193 return conditions, nil 194 } 195 196 // Matches returns true if the query matches against any event in the given set 197 // of events, false otherwise. For each event, a match exists if the query is 198 // matched against *any* value in a slice of values. An error is returned if 199 // any attempted event match returns an error. 200 // 201 // For example, query "name=John" matches events = {"name": ["John", "Eric"]}. 202 // More examples could be found in parser_test.go and query_test.go. 203 func (q *Query) Matches(rawEvents []types.Event) (bool, error) { 204 if len(rawEvents) == 0 { 205 return false, nil 206 } 207 208 events := flattenEvents(rawEvents) 209 210 var ( 211 eventAttr string 212 op Operator 213 ) 214 215 buffer, begin, end := q.parser.Buffer, 0, 0 216 217 // tokens must be in the following order: 218 219 // tag ("tx.gas") -> operator ("=") -> operand ("7") 220 for token := range q.parser.Tokens() { 221 switch token.pegRule { 222 case rulePegText: 223 begin, end = int(token.begin), int(token.end) 224 225 case ruletag: 226 eventAttr = buffer[begin:end] 227 228 case rulele: 229 op = OpLessEqual 230 231 case rulege: 232 op = OpGreaterEqual 233 234 case rulel: 235 op = OpLess 236 237 case ruleg: 238 op = OpGreater 239 240 case ruleequal: 241 op = OpEqual 242 243 case rulecontains: 244 op = OpContains 245 case ruleexists: 246 op = OpExists 247 if strings.Contains(eventAttr, ".") { 248 // Searching for a full "type.attribute" event. 249 _, ok := events[eventAttr] 250 if !ok { 251 return false, nil 252 } 253 } else { 254 foundEvent := false 255 256 loop: 257 for compositeKey := range events { 258 if strings.Index(compositeKey, eventAttr) == 0 { 259 foundEvent = true 260 break loop 261 } 262 } 263 if !foundEvent { 264 return false, nil 265 } 266 } 267 268 case rulevalue: 269 // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock") 270 valueWithoutSingleQuotes := buffer[begin+1 : end-1] 271 272 // see if the triplet (event attribute, operator, operand) matches any event 273 // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" } 274 match, err := match(eventAttr, op, reflect.ValueOf(valueWithoutSingleQuotes), events) 275 if err != nil { 276 return false, err 277 } 278 279 if !match { 280 return false, nil 281 } 282 283 case rulenumber: 284 number := buffer[begin:end] 285 if strings.ContainsAny(number, ".") { // if it looks like a floating-point number 286 value, err := strconv.ParseFloat(number, 64) 287 if err != nil { 288 err = fmt.Errorf( 289 "got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", 290 err, number, 291 ) 292 return false, err 293 } 294 295 match, err := match(eventAttr, op, reflect.ValueOf(value), events) 296 if err != nil { 297 return false, err 298 } 299 300 if !match { 301 return false, nil 302 } 303 } else { 304 value, err := strconv.ParseInt(number, 10, 64) 305 if err != nil { 306 err = fmt.Errorf( 307 "got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", 308 err, number, 309 ) 310 return false, err 311 } 312 313 match, err := match(eventAttr, op, reflect.ValueOf(value), events) 314 if err != nil { 315 return false, err 316 } 317 318 if !match { 319 return false, nil 320 } 321 } 322 323 case ruletime: 324 value, err := time.Parse(TimeLayout, buffer[begin:end]) 325 if err != nil { 326 err = fmt.Errorf( 327 "got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", 328 err, buffer[begin:end], 329 ) 330 return false, err 331 } 332 333 match, err := match(eventAttr, op, reflect.ValueOf(value), events) 334 if err != nil { 335 return false, err 336 } 337 338 if !match { 339 return false, nil 340 } 341 342 case ruledate: 343 value, err := time.Parse("2006-01-02", buffer[begin:end]) 344 if err != nil { 345 err = fmt.Errorf( 346 "got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", 347 err, buffer[begin:end], 348 ) 349 return false, err 350 } 351 352 match, err := match(eventAttr, op, reflect.ValueOf(value), events) 353 if err != nil { 354 return false, err 355 } 356 357 if !match { 358 return false, nil 359 } 360 } 361 } 362 363 return true, nil 364 } 365 366 // match returns true if the given triplet (attribute, operator, operand) matches 367 // any value in an event for that attribute. If any match fails with an error, 368 // that error is returned. 369 // 370 // First, it looks up the key in the events and if it finds one, tries to compare 371 // all the values from it to the operand using the operator. 372 // 373 // "tx.gas", "=", "7", {"tx": [{"gas": 7, "ID": "4AE393495334"}]} 374 func match(attr string, op Operator, operand reflect.Value, events map[string][]string) (bool, error) { 375 // look up the tag from the query in tags 376 values, ok := events[attr] 377 if !ok { 378 return false, nil 379 } 380 381 for _, value := range values { 382 // return true if any value in the set of the event's values matches 383 match, err := matchValue(value, op, operand) 384 if err != nil { 385 return false, err 386 } 387 388 if match { 389 return true, nil 390 } 391 } 392 393 return false, nil 394 } 395 396 // matchValue will attempt to match a string value against an operator an 397 // operand. A boolean is returned representing the match result. It will return 398 // an error if the value cannot be parsed and matched against the operand type. 399 func matchValue(value string, op Operator, operand reflect.Value) (bool, error) { 400 switch operand.Kind() { 401 case reflect.Struct: // time 402 operandAsTime := operand.Interface().(time.Time) 403 404 // try our best to convert value from events to time.Time 405 var ( 406 v time.Time 407 err error 408 ) 409 410 if strings.ContainsAny(value, "T") { 411 v, err = time.Parse(TimeLayout, value) 412 } else { 413 v, err = time.Parse(DateLayout, value) 414 } 415 if err != nil { 416 return false, fmt.Errorf("failed to convert value %v from event attribute to time.Time: %w", value, err) 417 } 418 419 switch op { 420 case OpLessEqual: 421 return (v.Before(operandAsTime) || v.Equal(operandAsTime)), nil 422 case OpGreaterEqual: 423 return (v.Equal(operandAsTime) || v.After(operandAsTime)), nil 424 case OpLess: 425 return v.Before(operandAsTime), nil 426 case OpGreater: 427 return v.After(operandAsTime), nil 428 case OpEqual: 429 return v.Equal(operandAsTime), nil 430 } 431 432 case reflect.Float64: 433 var v float64 434 435 operandFloat64 := operand.Interface().(float64) 436 filteredValue := numRegex.FindString(value) 437 438 // try our best to convert value from tags to float64 439 v, err := strconv.ParseFloat(filteredValue, 64) 440 if err != nil { 441 return false, fmt.Errorf("failed to convert value %v from event attribute to float64: %w", filteredValue, err) 442 } 443 444 switch op { 445 case OpLessEqual: 446 return v <= operandFloat64, nil 447 case OpGreaterEqual: 448 return v >= operandFloat64, nil 449 case OpLess: 450 return v < operandFloat64, nil 451 case OpGreater: 452 return v > operandFloat64, nil 453 case OpEqual: 454 return v == operandFloat64, nil 455 } 456 457 case reflect.Int64: 458 var v int64 459 460 operandInt := operand.Interface().(int64) 461 filteredValue := numRegex.FindString(value) 462 463 // if value looks like float, we try to parse it as float 464 if strings.ContainsAny(filteredValue, ".") { 465 v1, err := strconv.ParseFloat(filteredValue, 64) 466 if err != nil { 467 return false, fmt.Errorf("failed to convert value %v from event attribute to float64: %w", filteredValue, err) 468 } 469 470 v = int64(v1) 471 } else { 472 var err error 473 // try our best to convert value from tags to int64 474 v, err = strconv.ParseInt(filteredValue, 10, 64) 475 if err != nil { 476 return false, fmt.Errorf("failed to convert value %v from event attribute to int64: %w", filteredValue, err) 477 } 478 } 479 480 switch op { 481 case OpLessEqual: 482 return v <= operandInt, nil 483 case OpGreaterEqual: 484 return v >= operandInt, nil 485 case OpLess: 486 return v < operandInt, nil 487 case OpGreater: 488 return v > operandInt, nil 489 case OpEqual: 490 return v == operandInt, nil 491 } 492 493 case reflect.String: 494 switch op { 495 case OpEqual: 496 return value == operand.String(), nil 497 case OpContains: 498 return strings.Contains(value, operand.String()), nil 499 } 500 501 default: 502 return false, fmt.Errorf("unknown kind of operand %v", operand.Kind()) 503 } 504 505 return false, nil 506 } 507 508 func flattenEvents(events []types.Event) map[string][]string { 509 flattened := make(map[string][]string) 510 511 for _, event := range events { 512 if len(event.Type) == 0 { 513 continue 514 } 515 516 for _, attr := range event.Attributes { 517 if len(attr.Key) == 0 { 518 continue 519 } 520 521 compositeEvent := fmt.Sprintf("%s.%s", event.Type, attr.Key) 522 flattened[compositeEvent] = append(flattened[compositeEvent], attr.Value) 523 } 524 } 525 526 return flattened 527 }