forked from influxdata/influxdb
/
predicate_influxql.go
114 lines (99 loc) · 2.65 KB
/
predicate_influxql.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package storage
import (
"github.com/influxdata/influxdb/models"
"github.com/influxdata/influxql"
)
var measurementRemap = map[string]string{
"_measurement": "_name",
models.MeasurementTagKey: "_name",
models.FieldKeyTagKey: "_field",
}
func RewriteExprRemoveFieldKeyAndValue(expr influxql.Expr) influxql.Expr {
return influxql.RewriteExpr(expr, func(expr influxql.Expr) influxql.Expr {
if be, ok := expr.(*influxql.BinaryExpr); ok {
if ref, ok := be.LHS.(*influxql.VarRef); ok {
if ref.Val == "_field" || ref.Val == "$" {
return &influxql.BooleanLiteral{Val: true}
}
}
}
return expr
})
}
// HasSingleMeasurementNoOR determines if an index optimisation is available.
//
// Typically the read service will use the query engine to retrieve all field
// keys for all measurements that match the expression, which can be very
// inefficient if it can be proved that only one measurement matches the expression.
//
// This condition is determined when the following is true:
//
// * there is only one occurrence of the tag key `_measurement`.
// * there are no OR operators in the expression tree.
// * the operator for the `_measurement` binary expression is ==.
//
func HasSingleMeasurementNoOR(expr influxql.Expr) (string, bool) {
var lastMeasurement string
foundOnce := true
var invalidOP bool
influxql.WalkFunc(expr, func(node influxql.Node) {
if !foundOnce || invalidOP {
return
}
if be, ok := node.(*influxql.BinaryExpr); ok {
if be.Op == influxql.OR {
invalidOP = true
return
}
if ref, ok := be.LHS.(*influxql.VarRef); ok {
if ref.Val == measurementRemap[measurementKey] {
if be.Op != influxql.EQ {
invalidOP = true
return
}
if lastMeasurement != "" {
foundOnce = false
}
// Check that RHS is a literal string
if ref, ok := be.RHS.(*influxql.StringLiteral); ok {
lastMeasurement = ref.Val
}
}
}
}
})
return lastMeasurement, len(lastMeasurement) > 0 && foundOnce && !invalidOP
}
type hasRefs struct {
refs []string
found []bool
}
func (v *hasRefs) allFound() bool {
for _, val := range v.found {
if !val {
return false
}
}
return true
}
func (v *hasRefs) Visit(node influxql.Node) influxql.Visitor {
if v.allFound() {
return nil
}
if n, ok := node.(*influxql.VarRef); ok {
for i, r := range v.refs {
if !v.found[i] && r == n.Val {
v.found[i] = true
if v.allFound() {
return nil
}
}
}
}
return v
}
func HasFieldKeyOrValue(expr influxql.Expr) (bool, bool) {
refs := hasRefs{refs: []string{fieldKey, "$"}, found: make([]bool, 2)}
influxql.Walk(&refs, expr)
return refs.found[0], refs.found[1]
}