You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
9.4 KiB

  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. import (
  11. "fmt"
  12. "reflect"
  13. "strconv"
  14. "strings"
  15. "time"
  16. "github.com/tendermint/tendermint/libs/pubsub"
  17. )
  18. // Query holds the query string and the query parser.
  19. type Query struct {
  20. str string
  21. parser *QueryParser
  22. }
  23. // Condition represents a single condition within a query and consists of tag
  24. // (e.g. "tx.gas"), operator (e.g. "=") and operand (e.g. "7").
  25. type Condition struct {
  26. Tag string
  27. Op Operator
  28. Operand interface{}
  29. }
  30. // New parses the given string and returns a query or error if the string is
  31. // invalid.
  32. func New(s string) (*Query, error) {
  33. p := &QueryParser{Buffer: fmt.Sprintf(`"%s"`, s)}
  34. p.Init()
  35. if err := p.Parse(); err != nil {
  36. return nil, err
  37. }
  38. return &Query{str: s, parser: p}, nil
  39. }
  40. // MustParse turns the given string into a query or panics; for tests or others
  41. // cases where you know the string is valid.
  42. func MustParse(s string) *Query {
  43. q, err := New(s)
  44. if err != nil {
  45. panic(fmt.Sprintf("failed to parse %s: %v", s, err))
  46. }
  47. return q
  48. }
  49. // String returns the original string.
  50. func (q *Query) String() string {
  51. return q.str
  52. }
  53. // Operator is an operator that defines some kind of relation between tag and
  54. // operand (equality, etc.).
  55. type Operator uint8
  56. const (
  57. // "<="
  58. OpLessEqual Operator = iota
  59. // ">="
  60. OpGreaterEqual
  61. // "<"
  62. OpLess
  63. // ">"
  64. OpGreater
  65. // "="
  66. OpEqual
  67. // "CONTAINS"; used to check if a string contains a certain sub string.
  68. OpContains
  69. )
  70. // Conditions returns a list of conditions.
  71. func (q *Query) Conditions() []Condition {
  72. conditions := make([]Condition, 0)
  73. buffer, begin, end := q.parser.Buffer, 0, 0
  74. var tag string
  75. var op Operator
  76. // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7")
  77. for _, token := range q.parser.Tokens() {
  78. switch token.pegRule {
  79. case rulePegText:
  80. begin, end = int(token.begin), int(token.end)
  81. case ruletag:
  82. tag = buffer[begin:end]
  83. case rulele:
  84. op = OpLessEqual
  85. case rulege:
  86. op = OpGreaterEqual
  87. case rulel:
  88. op = OpLess
  89. case ruleg:
  90. op = OpGreater
  91. case ruleequal:
  92. op = OpEqual
  93. case rulecontains:
  94. op = OpContains
  95. case rulevalue:
  96. // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock")
  97. valueWithoutSingleQuotes := buffer[begin+1 : end-1]
  98. conditions = append(conditions, Condition{tag, op, valueWithoutSingleQuotes})
  99. case rulenumber:
  100. number := buffer[begin:end]
  101. if strings.Contains(number, ".") { // if it looks like a floating-point number
  102. value, err := strconv.ParseFloat(number, 64)
  103. if err != nil {
  104. panic(fmt.Sprintf("got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", err, number))
  105. }
  106. conditions = append(conditions, Condition{tag, op, value})
  107. } else {
  108. value, err := strconv.ParseInt(number, 10, 64)
  109. if err != nil {
  110. panic(fmt.Sprintf("got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", err, number))
  111. }
  112. conditions = append(conditions, Condition{tag, op, value})
  113. }
  114. case ruletime:
  115. value, err := time.Parse(time.RFC3339, buffer[begin:end])
  116. if err != nil {
  117. panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", err, buffer[begin:end]))
  118. }
  119. conditions = append(conditions, Condition{tag, op, value})
  120. case ruledate:
  121. value, err := time.Parse("2006-01-02", buffer[begin:end])
  122. if err != nil {
  123. panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", err, buffer[begin:end]))
  124. }
  125. conditions = append(conditions, Condition{tag, op, value})
  126. }
  127. }
  128. return conditions
  129. }
  130. // Matches returns true if the query matches the given set of tags, false otherwise.
  131. //
  132. // For example, query "name=John" matches tags = {"name": "John"}. More
  133. // examples could be found in parser_test.go and query_test.go.
  134. func (q *Query) Matches(tags pubsub.TagMap) bool {
  135. if tags.Len() == 0 {
  136. return false
  137. }
  138. buffer, begin, end := q.parser.Buffer, 0, 0
  139. var tag string
  140. var op Operator
  141. // tokens must be in the following order: tag ("tx.gas") -> operator ("=") -> operand ("7")
  142. for _, token := range q.parser.Tokens() {
  143. switch token.pegRule {
  144. case rulePegText:
  145. begin, end = int(token.begin), int(token.end)
  146. case ruletag:
  147. tag = buffer[begin:end]
  148. case rulele:
  149. op = OpLessEqual
  150. case rulege:
  151. op = OpGreaterEqual
  152. case rulel:
  153. op = OpLess
  154. case ruleg:
  155. op = OpGreater
  156. case ruleequal:
  157. op = OpEqual
  158. case rulecontains:
  159. op = OpContains
  160. case rulevalue:
  161. // strip single quotes from value (i.e. "'NewBlock'" -> "NewBlock")
  162. valueWithoutSingleQuotes := buffer[begin+1 : end-1]
  163. // see if the triplet (tag, operator, operand) matches any tag
  164. // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" }
  165. if !match(tag, op, reflect.ValueOf(valueWithoutSingleQuotes), tags) {
  166. return false
  167. }
  168. case rulenumber:
  169. number := buffer[begin:end]
  170. if strings.Contains(number, ".") { // if it looks like a floating-point number
  171. value, err := strconv.ParseFloat(number, 64)
  172. if err != nil {
  173. panic(fmt.Sprintf("got %v while trying to parse %s as float64 (should never happen if the grammar is correct)", err, number))
  174. }
  175. if !match(tag, op, reflect.ValueOf(value), tags) {
  176. return false
  177. }
  178. } else {
  179. value, err := strconv.ParseInt(number, 10, 64)
  180. if err != nil {
  181. panic(fmt.Sprintf("got %v while trying to parse %s as int64 (should never happen if the grammar is correct)", err, number))
  182. }
  183. if !match(tag, op, reflect.ValueOf(value), tags) {
  184. return false
  185. }
  186. }
  187. case ruletime:
  188. value, err := time.Parse(time.RFC3339, buffer[begin:end])
  189. if err != nil {
  190. panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / RFC3339 (should never happen if the grammar is correct)", err, buffer[begin:end]))
  191. }
  192. if !match(tag, op, reflect.ValueOf(value), tags) {
  193. return false
  194. }
  195. case ruledate:
  196. value, err := time.Parse("2006-01-02", buffer[begin:end])
  197. if err != nil {
  198. panic(fmt.Sprintf("got %v while trying to parse %s as time.Time / '2006-01-02' (should never happen if the grammar is correct)", err, buffer[begin:end]))
  199. }
  200. if !match(tag, op, reflect.ValueOf(value), tags) {
  201. return false
  202. }
  203. }
  204. }
  205. return true
  206. }
  207. // match returns true if the given triplet (tag, operator, operand) matches any tag.
  208. //
  209. // First, it looks up the tag in tags and if it finds one, tries to compare the
  210. // value from it to the operand using the operator.
  211. //
  212. // "tx.gas", "=", "7", { "tx.gas": 7, "tx.ID": "4AE393495334" }
  213. func match(tag string, op Operator, operand reflect.Value, tags pubsub.TagMap) bool {
  214. // look up the tag from the query in tags
  215. value, ok := tags.Get(tag)
  216. if !ok {
  217. return false
  218. }
  219. switch operand.Kind() {
  220. case reflect.Struct: // time
  221. operandAsTime := operand.Interface().(time.Time)
  222. v, ok := value.(time.Time)
  223. if !ok { // if value from tags is not time.Time
  224. return false
  225. }
  226. switch op {
  227. case OpLessEqual:
  228. return v.Before(operandAsTime) || v.Equal(operandAsTime)
  229. case OpGreaterEqual:
  230. return v.Equal(operandAsTime) || v.After(operandAsTime)
  231. case OpLess:
  232. return v.Before(operandAsTime)
  233. case OpGreater:
  234. return v.After(operandAsTime)
  235. case OpEqual:
  236. return v.Equal(operandAsTime)
  237. }
  238. case reflect.Float64:
  239. operandFloat64 := operand.Interface().(float64)
  240. var v float64
  241. // try our best to convert value from tags to float64
  242. switch vt := value.(type) {
  243. case float64:
  244. v = vt
  245. case float32:
  246. v = float64(vt)
  247. case int:
  248. v = float64(vt)
  249. case int8:
  250. v = float64(vt)
  251. case int16:
  252. v = float64(vt)
  253. case int32:
  254. v = float64(vt)
  255. case int64:
  256. v = float64(vt)
  257. default: // fail for all other types
  258. panic(fmt.Sprintf("Incomparable types: %T (%v) vs float64 (%v)", value, value, operandFloat64))
  259. }
  260. switch op {
  261. case OpLessEqual:
  262. return v <= operandFloat64
  263. case OpGreaterEqual:
  264. return v >= operandFloat64
  265. case OpLess:
  266. return v < operandFloat64
  267. case OpGreater:
  268. return v > operandFloat64
  269. case OpEqual:
  270. return v == operandFloat64
  271. }
  272. case reflect.Int64:
  273. operandInt := operand.Interface().(int64)
  274. var v int64
  275. // try our best to convert value from tags to int64
  276. switch vt := value.(type) {
  277. case int64:
  278. v = vt
  279. case int8:
  280. v = int64(vt)
  281. case int16:
  282. v = int64(vt)
  283. case int32:
  284. v = int64(vt)
  285. case int:
  286. v = int64(vt)
  287. case float64:
  288. v = int64(vt)
  289. case float32:
  290. v = int64(vt)
  291. default: // fail for all other types
  292. panic(fmt.Sprintf("Incomparable types: %T (%v) vs int64 (%v)", value, value, operandInt))
  293. }
  294. switch op {
  295. case OpLessEqual:
  296. return v <= operandInt
  297. case OpGreaterEqual:
  298. return v >= operandInt
  299. case OpLess:
  300. return v < operandInt
  301. case OpGreater:
  302. return v > operandInt
  303. case OpEqual:
  304. return v == operandInt
  305. }
  306. case reflect.String:
  307. v, ok := value.(string)
  308. if !ok { // if value from tags is not string
  309. return false
  310. }
  311. switch op {
  312. case OpEqual:
  313. return v == operand.String()
  314. case OpContains:
  315. return strings.Contains(v, operand.String())
  316. }
  317. default:
  318. panic(fmt.Sprintf("Unknown kind of operand %v", operand.Kind()))
  319. }
  320. return false
  321. }