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  }