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