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 || idx >= len(fe.Args) {
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
}
|