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