-
Notifications
You must be signed in to change notification settings - Fork 32
/
utils.go
138 lines (132 loc) · 3.02 KB
/
utils.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package metricsql
import (
"fmt"
"strings"
)
// ExpandWithExprs expands WITH expressions inside q and returns the resulting
// PromQL without WITH expressions.
func ExpandWithExprs(q string) (string, error) {
e, err := Parse(q)
if err != nil {
return "", err
}
buf := e.AppendString(nil)
return string(buf), nil
}
// VisitAll recursively calls f for all the Expr children in e.
//
// It visits leaf children at first and then visits parent nodes.
// It is safe modifying expr in f.
func VisitAll(e Expr, f func(expr Expr)) {
switch expr := e.(type) {
case *BinaryOpExpr:
VisitAll(expr.Left, f)
VisitAll(expr.Right, f)
VisitAll(&expr.GroupModifier, f)
VisitAll(&expr.JoinModifier, f)
case *FuncExpr:
for _, arg := range expr.Args {
VisitAll(arg, f)
}
case *AggrFuncExpr:
for _, arg := range expr.Args {
VisitAll(arg, f)
}
VisitAll(&expr.Modifier, f)
case *RollupExpr:
VisitAll(expr.Expr, f)
if expr.Window != nil {
VisitAll(expr.Window, f)
}
if expr.Step != nil {
VisitAll(expr.Step, f)
}
if expr.Offset != nil {
VisitAll(expr.Offset, f)
}
if expr.At != nil {
VisitAll(expr.At, f)
}
}
f(e)
}
// IsLikelyInvalid returns true if e contains tricky implicit conversion, which is invalid most of the time.
//
// Examples of invalid expressions:
//
// rate(sum(foo))
// rate(abs(foo))
// rate(foo + bar)
// rate(foo > 10)
//
// These expressions are implicitly converted into another expressions, which returns unexpected results most of the time:
//
// rate(default_rollup(sum(foo))[1i:1i])
// rate(default_rollup(abs(foo))[1i:1i])
// rate(default_rollup(foo + bar)[1i:1i])
// rate(default_rollup(foo > 10)[1i:1i])
//
// See https://docs.victoriametrics.com/metricsql/#implicit-query-conversions
//
// Note that rate(foo) is valid expression, since it returns the expected results most of the time, e.g. rate(foo[1i]).
func IsLikelyInvalid(e Expr) bool {
hasImplicitConversion := false
VisitAll(e, func(expr Expr) {
if hasImplicitConversion {
return
}
fe, ok := expr.(*FuncExpr)
if !ok {
return
}
idx := GetRollupArgIdx(fe)
if idx < 0 {
return
}
arg := fe.Args[idx]
re, ok := arg.(*RollupExpr)
if !ok {
if _, ok = arg.(*MetricExpr); !ok {
hasImplicitConversion = true
}
return
}
if _, ok := re.Expr.(*MetricExpr); ok {
return
}
if re.Window == nil {
hasImplicitConversion = true
}
})
return hasImplicitConversion
}
// IsSupportedFunction returns true if funcName contains supported MetricsQL function
func IsSupportedFunction(funcName string) bool {
funcName = strings.ToLower(funcName)
if IsRollupFunc(funcName) {
return true
}
if IsTransformFunc(funcName) {
return true
}
if IsAggrFunc(funcName) {
return true
}
return false
}
func checkSupportedFunctions(e Expr) error {
var err error
VisitAll(e, func(expr Expr) {
if err != nil {
return
}
fe, ok := expr.(*FuncExpr)
if !ok {
return
}
if !IsRollupFunc(fe.Name) && !IsTransformFunc(fe.Name) {
err = fmt.Errorf("unsupported function %q", fe.Name)
}
})
return err
}