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