mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-11-21 14:44:00 +00:00
Rename lib/promql to lib/metricsql and apply small fixes
This commit is contained in:
parent
bec62e4e43
commit
1925ee038d
35 changed files with 2343 additions and 2440 deletions
|
@ -15,6 +15,7 @@ import (
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/promql"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
"github.com/VictoriaMetrics/metrics"
|
"github.com/VictoriaMetrics/metrics"
|
||||||
"github.com/valyala/quicktemplate"
|
"github.com/valyala/quicktemplate"
|
||||||
|
@ -634,14 +635,14 @@ func parseDuration(s string, step int64) (int64, error) {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
return promql.DurationValue(s, step)
|
return metricsql.DurationValue(s, step)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePositiveDuration(s string, step int64) (int64, error) {
|
func parsePositiveDuration(s string, step int64) (int64, error) {
|
||||||
if len(s) == 0 {
|
if len(s) == 0 {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
return promql.PositiveDurationValue(s, step)
|
return metricsql.PositiveDurationValue(s, step)
|
||||||
}
|
}
|
||||||
|
|
||||||
// QueryRangeHandler processes /api/v1/query_range request.
|
// QueryRangeHandler processes /api/v1/query_range request.
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
"github.com/VictoriaMetrics/metrics"
|
"github.com/VictoriaMetrics/metrics"
|
||||||
"github.com/valyala/histogram"
|
"github.com/valyala/histogram"
|
||||||
|
@ -49,7 +49,7 @@ type aggrFunc func(afa *aggrFuncArg) ([]*timeseries, error)
|
||||||
|
|
||||||
type aggrFuncArg struct {
|
type aggrFuncArg struct {
|
||||||
args [][]*timeseries
|
args [][]*timeseries
|
||||||
ae *promql.AggrFuncExpr
|
ae *metricsql.AggrFuncExpr
|
||||||
ec *EvalConfig
|
ec *EvalConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +68,7 @@ func newAggrFunc(afe func(tss []*timeseries) []*timeseries) aggrFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeGroupTags(metricName *storage.MetricName, modifier *promql.ModifierExpr) {
|
func removeGroupTags(metricName *storage.MetricName, modifier *metricsql.ModifierExpr) {
|
||||||
groupOp := strings.ToLower(modifier.Op)
|
groupOp := strings.ToLower(modifier.Op)
|
||||||
switch groupOp {
|
switch groupOp {
|
||||||
case "", "by":
|
case "", "by":
|
||||||
|
@ -80,7 +80,7 @@ func removeGroupTags(metricName *storage.MetricName, modifier *promql.ModifierEx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func aggrFuncExt(afe func(tss []*timeseries) []*timeseries, argOrig []*timeseries, modifier *promql.ModifierExpr, keepOriginal bool) ([]*timeseries, error) {
|
func aggrFuncExt(afe func(tss []*timeseries) []*timeseries, argOrig []*timeseries, modifier *metricsql.ModifierExpr, keepOriginal bool) ([]*timeseries, error) {
|
||||||
arg := copyTimeseriesMetricNames(argOrig)
|
arg := copyTimeseriesMetricNames(argOrig)
|
||||||
|
|
||||||
// Perform grouping.
|
// Perform grouping.
|
||||||
|
|
|
@ -5,11 +5,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
// callbacks for optimized incremental calculations for aggregate functions
|
// callbacks for optimized incremental calculations for aggregate functions
|
||||||
// over rollups over metricExpr.
|
// over rollups over metricsql.MetricExpr.
|
||||||
//
|
//
|
||||||
// These calculations save RAM for aggregates over big number of time series.
|
// These calculations save RAM for aggregates over big number of time series.
|
||||||
var incrementalAggrFuncCallbacksMap = map[string]*incrementalAggrFuncCallbacks{
|
var incrementalAggrFuncCallbacksMap = map[string]*incrementalAggrFuncCallbacks{
|
||||||
|
@ -51,7 +51,7 @@ var incrementalAggrFuncCallbacksMap = map[string]*incrementalAggrFuncCallbacks{
|
||||||
}
|
}
|
||||||
|
|
||||||
type incrementalAggrFuncContext struct {
|
type incrementalAggrFuncContext struct {
|
||||||
ae *promql.AggrFuncExpr
|
ae *metricsql.AggrFuncExpr
|
||||||
|
|
||||||
mLock sync.Mutex
|
mLock sync.Mutex
|
||||||
m map[uint]map[string]*incrementalAggrContext
|
m map[uint]map[string]*incrementalAggrContext
|
||||||
|
@ -59,7 +59,7 @@ type incrementalAggrFuncContext struct {
|
||||||
callbacks *incrementalAggrFuncCallbacks
|
callbacks *incrementalAggrFuncCallbacks
|
||||||
}
|
}
|
||||||
|
|
||||||
func newIncrementalAggrFuncContext(ae *promql.AggrFuncExpr, callbacks *incrementalAggrFuncCallbacks) *incrementalAggrFuncContext {
|
func newIncrementalAggrFuncContext(ae *metricsql.AggrFuncExpr, callbacks *incrementalAggrFuncCallbacks) *incrementalAggrFuncContext {
|
||||||
return &incrementalAggrFuncContext{
|
return &incrementalAggrFuncContext{
|
||||||
ae: ae,
|
ae: ae,
|
||||||
m: make(map[uint]map[string]*incrementalAggrContext),
|
m: make(map[uint]map[string]*incrementalAggrContext),
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIncrementalAggr(t *testing.T) {
|
func TestIncrementalAggr(t *testing.T) {
|
||||||
|
@ -44,7 +44,7 @@ func TestIncrementalAggr(t *testing.T) {
|
||||||
f := func(name string, valuesExpected []float64) {
|
f := func(name string, valuesExpected []float64) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
callbacks := getIncrementalAggrFuncCallbacks(name)
|
callbacks := getIncrementalAggrFuncCallbacks(name)
|
||||||
ae := &promql.AggrFuncExpr{
|
ae := &metricsql.AggrFuncExpr{
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
tssExpected := []*timeseries{{
|
tssExpected := []*timeseries{{
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package promql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
|
||||||
)
|
|
||||||
|
|
||||||
// DurationValue returns the duration in milliseconds for the given s
|
|
||||||
// and the given step.
|
|
||||||
var DurationValue = promql.DurationValue
|
|
|
@ -6,25 +6,26 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql/binaryop"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
var binaryOpFuncs = map[string]binaryOpFunc{
|
var binaryOpFuncs = map[string]binaryOpFunc{
|
||||||
"+": newBinaryOpArithFunc(binaryOpPlus),
|
"+": newBinaryOpArithFunc(binaryop.Plus),
|
||||||
"-": newBinaryOpArithFunc(binaryOpMinus),
|
"-": newBinaryOpArithFunc(binaryop.Minus),
|
||||||
"*": newBinaryOpArithFunc(binaryOpMul),
|
"*": newBinaryOpArithFunc(binaryop.Mul),
|
||||||
"/": newBinaryOpArithFunc(binaryOpDiv),
|
"/": newBinaryOpArithFunc(binaryop.Div),
|
||||||
"%": newBinaryOpArithFunc(binaryOpMod),
|
"%": newBinaryOpArithFunc(binaryop.Mod),
|
||||||
"^": newBinaryOpArithFunc(binaryOpPow),
|
"^": newBinaryOpArithFunc(binaryop.Pow),
|
||||||
|
|
||||||
// cmp ops
|
// cmp ops
|
||||||
"==": newBinaryOpCmpFunc(binaryOpEq),
|
"==": newBinaryOpCmpFunc(binaryop.Eq),
|
||||||
"!=": newBinaryOpCmpFunc(binaryOpNeq),
|
"!=": newBinaryOpCmpFunc(binaryop.Neq),
|
||||||
">": newBinaryOpCmpFunc(binaryOpGt),
|
">": newBinaryOpCmpFunc(binaryop.Gt),
|
||||||
"<": newBinaryOpCmpFunc(binaryOpLt),
|
"<": newBinaryOpCmpFunc(binaryop.Lt),
|
||||||
">=": newBinaryOpCmpFunc(binaryOpGte),
|
">=": newBinaryOpCmpFunc(binaryop.Gte),
|
||||||
"<=": newBinaryOpCmpFunc(binaryOpLte),
|
"<=": newBinaryOpCmpFunc(binaryop.Lte),
|
||||||
|
|
||||||
// logical set ops
|
// logical set ops
|
||||||
"and": binaryOpAnd,
|
"and": binaryOpAnd,
|
||||||
|
@ -32,9 +33,9 @@ var binaryOpFuncs = map[string]binaryOpFunc{
|
||||||
"unless": binaryOpUnless,
|
"unless": binaryOpUnless,
|
||||||
|
|
||||||
// New op
|
// New op
|
||||||
"if": newBinaryOpArithFunc(binaryOpIf),
|
"if": newBinaryOpArithFunc(binaryop.If),
|
||||||
"ifnot": newBinaryOpArithFunc(binaryOpIfnot),
|
"ifnot": newBinaryOpArithFunc(binaryop.Ifnot),
|
||||||
"default": newBinaryOpArithFunc(binaryOpDefault),
|
"default": newBinaryOpArithFunc(binaryop.Default),
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBinaryOpFunc(op string) binaryOpFunc {
|
func getBinaryOpFunc(op string) binaryOpFunc {
|
||||||
|
@ -42,80 +43,8 @@ func getBinaryOpFunc(op string) binaryOpFunc {
|
||||||
return binaryOpFuncs[op]
|
return binaryOpFuncs[op]
|
||||||
}
|
}
|
||||||
|
|
||||||
func isBinaryOpCmp(op string) bool {
|
|
||||||
switch op {
|
|
||||||
case "==", "!=", ">", "<", ">=", "<=":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpConstants(op string, left, right float64, isBool bool) float64 {
|
|
||||||
if isBinaryOpCmp(op) {
|
|
||||||
evalCmp := func(cf func(left, right float64) bool) float64 {
|
|
||||||
if isBool {
|
|
||||||
if cf(left, right) {
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if cf(left, right) {
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
return nan
|
|
||||||
}
|
|
||||||
switch op {
|
|
||||||
case "==":
|
|
||||||
left = evalCmp(binaryOpEq)
|
|
||||||
case "!=":
|
|
||||||
left = evalCmp(binaryOpNeq)
|
|
||||||
case ">":
|
|
||||||
left = evalCmp(binaryOpGt)
|
|
||||||
case "<":
|
|
||||||
left = evalCmp(binaryOpLt)
|
|
||||||
case ">=":
|
|
||||||
left = evalCmp(binaryOpGte)
|
|
||||||
case "<=":
|
|
||||||
left = evalCmp(binaryOpLte)
|
|
||||||
default:
|
|
||||||
logger.Panicf("BUG: unexpected comparison binaryOp: %q", op)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch op {
|
|
||||||
case "+":
|
|
||||||
left = binaryOpPlus(left, right)
|
|
||||||
case "-":
|
|
||||||
left = binaryOpMinus(left, right)
|
|
||||||
case "*":
|
|
||||||
left = binaryOpMul(left, right)
|
|
||||||
case "/":
|
|
||||||
left = binaryOpDiv(left, right)
|
|
||||||
case "%":
|
|
||||||
left = binaryOpMod(left, right)
|
|
||||||
case "^":
|
|
||||||
left = binaryOpPow(left, right)
|
|
||||||
case "and":
|
|
||||||
// Nothing to do
|
|
||||||
case "or":
|
|
||||||
// Nothing to do
|
|
||||||
case "unless":
|
|
||||||
left = nan
|
|
||||||
case "default":
|
|
||||||
left = binaryOpDefault(left, right)
|
|
||||||
case "if":
|
|
||||||
left = binaryOpIf(left, right)
|
|
||||||
case "ifnot":
|
|
||||||
left = binaryOpIfnot(left, right)
|
|
||||||
default:
|
|
||||||
logger.Panicf("BUG: unexpected non-comparison binaryOp: %q", op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
type binaryOpFuncArg struct {
|
type binaryOpFuncArg struct {
|
||||||
be *promql.BinaryOpExpr
|
be *metricsql.BinaryOpExpr
|
||||||
left []*timeseries
|
left []*timeseries
|
||||||
right []*timeseries
|
right []*timeseries
|
||||||
}
|
}
|
||||||
|
@ -175,7 +104,7 @@ func newBinaryOpFunc(bf func(left, right float64, isBool bool) float64) binaryOp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func adjustBinaryOpTags(be *promql.BinaryOpExpr, left, right []*timeseries) ([]*timeseries, []*timeseries, []*timeseries, error) {
|
func adjustBinaryOpTags(be *metricsql.BinaryOpExpr, left, right []*timeseries) ([]*timeseries, []*timeseries, []*timeseries, error) {
|
||||||
if len(be.GroupModifier.Op) == 0 && len(be.JoinModifier.Op) == 0 {
|
if len(be.GroupModifier.Op) == 0 && len(be.JoinModifier.Op) == 0 {
|
||||||
if isScalar(left) {
|
if isScalar(left) {
|
||||||
// Fast path: `scalar op vector`
|
// Fast path: `scalar op vector`
|
||||||
|
@ -256,7 +185,7 @@ func adjustBinaryOpTags(be *promql.BinaryOpExpr, left, right []*timeseries) ([]*
|
||||||
return rvsLeft, rvsRight, dst, nil
|
return rvsLeft, rvsRight, dst, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ensureSingleTimeseries(side string, be *promql.BinaryOpExpr, tss []*timeseries) error {
|
func ensureSingleTimeseries(side string, be *metricsql.BinaryOpExpr, tss []*timeseries) error {
|
||||||
if len(tss) == 0 {
|
if len(tss) == 0 {
|
||||||
logger.Panicf("BUG: tss must contain at least one value")
|
logger.Panicf("BUG: tss must contain at least one value")
|
||||||
}
|
}
|
||||||
|
@ -270,7 +199,7 @@ func ensureSingleTimeseries(side string, be *promql.BinaryOpExpr, tss []*timeser
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func groupJoin(singleTimeseriesSide string, be *promql.BinaryOpExpr, rvsLeft, rvsRight, tssLeft, tssRight []*timeseries) ([]*timeseries, []*timeseries, error) {
|
func groupJoin(singleTimeseriesSide string, be *metricsql.BinaryOpExpr, rvsLeft, rvsRight, tssLeft, tssRight []*timeseries) ([]*timeseries, []*timeseries, error) {
|
||||||
joinTags := be.JoinModifier.Args
|
joinTags := be.JoinModifier.Args
|
||||||
var m map[string]*timeseries
|
var m map[string]*timeseries
|
||||||
for _, tsLeft := range tssLeft {
|
for _, tsLeft := range tssLeft {
|
||||||
|
@ -340,8 +269,8 @@ func mergeNonOverlappingTimeseries(dst, src *timeseries) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func resetMetricGroupIfRequired(be *promql.BinaryOpExpr, ts *timeseries) {
|
func resetMetricGroupIfRequired(be *metricsql.BinaryOpExpr, ts *timeseries) {
|
||||||
if isBinaryOpCmp(be.Op) && !be.Bool {
|
if metricsql.IsBinaryOpCmp(be.Op) && !be.Bool {
|
||||||
// Do not reset MetricGroup for non-boolean `compare` binary ops like Prometheus does.
|
// Do not reset MetricGroup for non-boolean `compare` binary ops like Prometheus does.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -353,90 +282,6 @@ func resetMetricGroupIfRequired(be *promql.BinaryOpExpr, ts *timeseries) {
|
||||||
ts.MetricName.ResetMetricGroup()
|
ts.MetricName.ResetMetricGroup()
|
||||||
}
|
}
|
||||||
|
|
||||||
func binaryOpPlus(left, right float64) float64 {
|
|
||||||
return left + right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpMinus(left, right float64) float64 {
|
|
||||||
return left - right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpMul(left, right float64) float64 {
|
|
||||||
return left * right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpDiv(left, right float64) float64 {
|
|
||||||
return left / right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpMod(left, right float64) float64 {
|
|
||||||
return math.Mod(left, right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpPow(left, right float64) float64 {
|
|
||||||
return math.Pow(left, right)
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpDefault(left, right float64) float64 {
|
|
||||||
if math.IsNaN(left) {
|
|
||||||
return right
|
|
||||||
}
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpIf(left, right float64) float64 {
|
|
||||||
if math.IsNaN(right) {
|
|
||||||
return nan
|
|
||||||
}
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpIfnot(left, right float64) float64 {
|
|
||||||
if math.IsNaN(right) {
|
|
||||||
return left
|
|
||||||
}
|
|
||||||
return nan
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpEq(left, right float64) bool {
|
|
||||||
// Special handling for nan == nan.
|
|
||||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/150 .
|
|
||||||
if math.IsNaN(left) {
|
|
||||||
return math.IsNaN(right)
|
|
||||||
}
|
|
||||||
|
|
||||||
return left == right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpNeq(left, right float64) bool {
|
|
||||||
// Special handling for comparison with nan.
|
|
||||||
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/150 .
|
|
||||||
if math.IsNaN(left) {
|
|
||||||
return !math.IsNaN(right)
|
|
||||||
}
|
|
||||||
if math.IsNaN(right) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return left != right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpGt(left, right float64) bool {
|
|
||||||
return left > right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpLt(left, right float64) bool {
|
|
||||||
return left < right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpGte(left, right float64) bool {
|
|
||||||
return left >= right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpLte(left, right float64) bool {
|
|
||||||
return left <= right
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpAnd(bfa *binaryOpFuncArg) ([]*timeseries, error) {
|
func binaryOpAnd(bfa *binaryOpFuncArg) ([]*timeseries, error) {
|
||||||
mLeft, mRight := createTimeseriesMapByTagSet(bfa.be, bfa.left, bfa.right)
|
mLeft, mRight := createTimeseriesMapByTagSet(bfa.be, bfa.left, bfa.right)
|
||||||
var rvs []*timeseries
|
var rvs []*timeseries
|
||||||
|
@ -473,7 +318,7 @@ func binaryOpUnless(bfa *binaryOpFuncArg) ([]*timeseries, error) {
|
||||||
return rvs, nil
|
return rvs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTimeseriesMapByTagSet(be *promql.BinaryOpExpr, left, right []*timeseries) (map[string][]*timeseries, map[string][]*timeseries) {
|
func createTimeseriesMapByTagSet(be *metricsql.BinaryOpExpr, left, right []*timeseries) (map[string][]*timeseries, map[string][]*timeseries) {
|
||||||
groupTags := be.GroupModifier.Args
|
groupTags := be.GroupModifier.Args
|
||||||
groupOp := strings.ToLower(be.GroupModifier.Op)
|
groupOp := strings.ToLower(be.GroupModifier.Op)
|
||||||
if len(groupOp) == 0 {
|
if len(groupOp) == 0 {
|
||||||
|
|
|
@ -6,13 +6,12 @@ import (
|
||||||
"math"
|
"math"
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
"github.com/VictoriaMetrics/metrics"
|
"github.com/VictoriaMetrics/metrics"
|
||||||
)
|
)
|
||||||
|
@ -154,9 +153,9 @@ func getTimestamps(start, end, step int64) []int64 {
|
||||||
return timestamps
|
return timestamps
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
func evalExpr(ec *EvalConfig, e metricsql.Expr) ([]*timeseries, error) {
|
||||||
if me, ok := e.(*promql.MetricExpr); ok {
|
if me, ok := e.(*metricsql.MetricExpr); ok {
|
||||||
re := &promql.RollupExpr{
|
re := &metricsql.RollupExpr{
|
||||||
Expr: me,
|
Expr: me,
|
||||||
}
|
}
|
||||||
rv, err := evalRollupFunc(ec, "default_rollup", rollupDefault, re, nil)
|
rv, err := evalRollupFunc(ec, "default_rollup", rollupDefault, re, nil)
|
||||||
|
@ -165,14 +164,14 @@ func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
||||||
}
|
}
|
||||||
return rv, nil
|
return rv, nil
|
||||||
}
|
}
|
||||||
if re, ok := e.(*promql.RollupExpr); ok {
|
if re, ok := e.(*metricsql.RollupExpr); ok {
|
||||||
rv, err := evalRollupFunc(ec, "default_rollup", rollupDefault, re, nil)
|
rv, err := evalRollupFunc(ec, "default_rollup", rollupDefault, re, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(`cannot evaluate %q: %s`, re.AppendString(nil), err)
|
return nil, fmt.Errorf(`cannot evaluate %q: %s`, re.AppendString(nil), err)
|
||||||
}
|
}
|
||||||
return rv, nil
|
return rv, nil
|
||||||
}
|
}
|
||||||
if fe, ok := e.(*promql.FuncExpr); ok {
|
if fe, ok := e.(*metricsql.FuncExpr); ok {
|
||||||
nrf := getRollupFunc(fe.Name)
|
nrf := getRollupFunc(fe.Name)
|
||||||
if nrf == nil {
|
if nrf == nil {
|
||||||
args, err := evalExprs(ec, fe.Args)
|
args, err := evalExprs(ec, fe.Args)
|
||||||
|
@ -208,11 +207,11 @@ func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
||||||
}
|
}
|
||||||
return rv, nil
|
return rv, nil
|
||||||
}
|
}
|
||||||
if ae, ok := e.(*promql.AggrFuncExpr); ok {
|
if ae, ok := e.(*metricsql.AggrFuncExpr); ok {
|
||||||
if callbacks := getIncrementalAggrFuncCallbacks(ae.Name); callbacks != nil {
|
if callbacks := getIncrementalAggrFuncCallbacks(ae.Name); callbacks != nil {
|
||||||
fe, nrf := tryGetArgRollupFuncWithMetricExpr(ae)
|
fe, nrf := tryGetArgRollupFuncWithMetricExpr(ae)
|
||||||
if fe != nil {
|
if fe != nil {
|
||||||
// There is an optimized path for calculating aggrFuncExpr over rollupFunc over metricExpr.
|
// There is an optimized path for calculating metricsql.AggrFuncExpr over rollupFunc over metricsql.MetricExpr.
|
||||||
// The optimized path saves RAM for aggregates over big number of time series.
|
// The optimized path saves RAM for aggregates over big number of time series.
|
||||||
args, re, err := evalRollupFuncArgs(ec, fe)
|
args, re, err := evalRollupFuncArgs(ec, fe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -245,7 +244,7 @@ func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
||||||
}
|
}
|
||||||
return rv, nil
|
return rv, nil
|
||||||
}
|
}
|
||||||
if be, ok := e.(*promql.BinaryOpExpr); ok {
|
if be, ok := e.(*metricsql.BinaryOpExpr); ok {
|
||||||
left, err := evalExpr(ec, be.Left)
|
left, err := evalExpr(ec, be.Left)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -269,18 +268,18 @@ func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
||||||
}
|
}
|
||||||
return rv, nil
|
return rv, nil
|
||||||
}
|
}
|
||||||
if ne, ok := e.(*promql.NumberExpr); ok {
|
if ne, ok := e.(*metricsql.NumberExpr); ok {
|
||||||
rv := evalNumber(ec, ne.N)
|
rv := evalNumber(ec, ne.N)
|
||||||
return rv, nil
|
return rv, nil
|
||||||
}
|
}
|
||||||
if se, ok := e.(*promql.StringExpr); ok {
|
if se, ok := e.(*metricsql.StringExpr); ok {
|
||||||
rv := evalString(ec, se.S)
|
rv := evalString(ec, se.S)
|
||||||
return rv, nil
|
return rv, nil
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unexpected expression %q", e.AppendString(nil))
|
return nil, fmt.Errorf("unexpected expression %q", e.AppendString(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryGetArgRollupFuncWithMetricExpr(ae *promql.AggrFuncExpr) (*promql.FuncExpr, newRollupFunc) {
|
func tryGetArgRollupFuncWithMetricExpr(ae *metricsql.AggrFuncExpr) (*metricsql.FuncExpr, newRollupFunc) {
|
||||||
if len(ae.Args) != 1 {
|
if len(ae.Args) != 1 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -291,31 +290,31 @@ func tryGetArgRollupFuncWithMetricExpr(ae *promql.AggrFuncExpr) (*promql.FuncExp
|
||||||
// - rollupFunc(metricExpr)
|
// - rollupFunc(metricExpr)
|
||||||
// - rollupFunc(metricExpr[d])
|
// - rollupFunc(metricExpr[d])
|
||||||
|
|
||||||
if me, ok := e.(*promql.MetricExpr); ok {
|
if me, ok := e.(*metricsql.MetricExpr); ok {
|
||||||
// e = metricExpr
|
// e = metricExpr
|
||||||
if me.IsEmpty() {
|
if me.IsEmpty() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
fe := &promql.FuncExpr{
|
fe := &metricsql.FuncExpr{
|
||||||
Name: "default_rollup",
|
Name: "default_rollup",
|
||||||
Args: []promql.Expr{me},
|
Args: []metricsql.Expr{me},
|
||||||
}
|
}
|
||||||
nrf := getRollupFunc(fe.Name)
|
nrf := getRollupFunc(fe.Name)
|
||||||
return fe, nrf
|
return fe, nrf
|
||||||
}
|
}
|
||||||
if re, ok := e.(*promql.RollupExpr); ok {
|
if re, ok := e.(*metricsql.RollupExpr); ok {
|
||||||
if me, ok := re.Expr.(*promql.MetricExpr); !ok || me.IsEmpty() || re.ForSubquery() {
|
if me, ok := re.Expr.(*metricsql.MetricExpr); !ok || me.IsEmpty() || re.ForSubquery() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
// e = metricExpr[d]
|
// e = metricExpr[d]
|
||||||
fe := &promql.FuncExpr{
|
fe := &metricsql.FuncExpr{
|
||||||
Name: "default_rollup",
|
Name: "default_rollup",
|
||||||
Args: []promql.Expr{re},
|
Args: []metricsql.Expr{re},
|
||||||
}
|
}
|
||||||
nrf := getRollupFunc(fe.Name)
|
nrf := getRollupFunc(fe.Name)
|
||||||
return fe, nrf
|
return fe, nrf
|
||||||
}
|
}
|
||||||
fe, ok := e.(*promql.FuncExpr)
|
fe, ok := e.(*metricsql.FuncExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -325,18 +324,18 @@ func tryGetArgRollupFuncWithMetricExpr(ae *promql.AggrFuncExpr) (*promql.FuncExp
|
||||||
}
|
}
|
||||||
rollupArgIdx := getRollupArgIdx(fe.Name)
|
rollupArgIdx := getRollupArgIdx(fe.Name)
|
||||||
arg := fe.Args[rollupArgIdx]
|
arg := fe.Args[rollupArgIdx]
|
||||||
if me, ok := arg.(*promql.MetricExpr); ok {
|
if me, ok := arg.(*metricsql.MetricExpr); ok {
|
||||||
if me.IsEmpty() {
|
if me.IsEmpty() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
// e = rollupFunc(metricExpr)
|
// e = rollupFunc(metricExpr)
|
||||||
return &promql.FuncExpr{
|
return &metricsql.FuncExpr{
|
||||||
Name: fe.Name,
|
Name: fe.Name,
|
||||||
Args: []promql.Expr{me},
|
Args: []metricsql.Expr{me},
|
||||||
}, nrf
|
}, nrf
|
||||||
}
|
}
|
||||||
if re, ok := arg.(*promql.RollupExpr); ok {
|
if re, ok := arg.(*metricsql.RollupExpr); ok {
|
||||||
if me, ok := re.Expr.(*promql.MetricExpr); !ok || me.IsEmpty() || re.ForSubquery() {
|
if me, ok := re.Expr.(*metricsql.MetricExpr); !ok || me.IsEmpty() || re.ForSubquery() {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
// e = rollupFunc(metricExpr[d])
|
// e = rollupFunc(metricExpr[d])
|
||||||
|
@ -345,7 +344,7 @@ func tryGetArgRollupFuncWithMetricExpr(ae *promql.AggrFuncExpr) (*promql.FuncExp
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalExprs(ec *EvalConfig, es []promql.Expr) ([][]*timeseries, error) {
|
func evalExprs(ec *EvalConfig, es []metricsql.Expr) ([][]*timeseries, error) {
|
||||||
var rvs [][]*timeseries
|
var rvs [][]*timeseries
|
||||||
for _, e := range es {
|
for _, e := range es {
|
||||||
rv, err := evalExpr(ec, e)
|
rv, err := evalExpr(ec, e)
|
||||||
|
@ -357,8 +356,8 @@ func evalExprs(ec *EvalConfig, es []promql.Expr) ([][]*timeseries, error) {
|
||||||
return rvs, nil
|
return rvs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalRollupFuncArgs(ec *EvalConfig, fe *promql.FuncExpr) ([]interface{}, *promql.RollupExpr, error) {
|
func evalRollupFuncArgs(ec *EvalConfig, fe *metricsql.FuncExpr) ([]interface{}, *metricsql.RollupExpr, error) {
|
||||||
var re *promql.RollupExpr
|
var re *metricsql.RollupExpr
|
||||||
rollupArgIdx := getRollupArgIdx(fe.Name)
|
rollupArgIdx := getRollupArgIdx(fe.Name)
|
||||||
args := make([]interface{}, len(fe.Args))
|
args := make([]interface{}, len(fe.Args))
|
||||||
for i, arg := range fe.Args {
|
for i, arg := range fe.Args {
|
||||||
|
@ -376,11 +375,11 @@ func evalRollupFuncArgs(ec *EvalConfig, fe *promql.FuncExpr) ([]interface{}, *pr
|
||||||
return args, re, nil
|
return args, re, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getRollupExprArg(arg promql.Expr) *promql.RollupExpr {
|
func getRollupExprArg(arg metricsql.Expr) *metricsql.RollupExpr {
|
||||||
re, ok := arg.(*promql.RollupExpr)
|
re, ok := arg.(*metricsql.RollupExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
// Wrap non-rollup arg into rollupExpr.
|
// Wrap non-rollup arg into metricsql.RollupExpr.
|
||||||
return &promql.RollupExpr{
|
return &metricsql.RollupExpr{
|
||||||
Expr: arg,
|
Expr: arg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -388,28 +387,28 @@ func getRollupExprArg(arg promql.Expr) *promql.RollupExpr {
|
||||||
// Return standard rollup if it doesn't contain subquery.
|
// Return standard rollup if it doesn't contain subquery.
|
||||||
return re
|
return re
|
||||||
}
|
}
|
||||||
me, ok := re.Expr.(*promql.MetricExpr)
|
me, ok := re.Expr.(*metricsql.MetricExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
// arg contains subquery.
|
// arg contains subquery.
|
||||||
return re
|
return re
|
||||||
}
|
}
|
||||||
// Convert me[w:step] -> default_rollup(me)[w:step]
|
// Convert me[w:step] -> default_rollup(me)[w:step]
|
||||||
reNew := *re
|
reNew := *re
|
||||||
reNew.Expr = &promql.FuncExpr{
|
reNew.Expr = &metricsql.FuncExpr{
|
||||||
Name: "default_rollup",
|
Name: "default_rollup",
|
||||||
Args: []promql.Expr{
|
Args: []metricsql.Expr{
|
||||||
&promql.RollupExpr{Expr: me},
|
&metricsql.RollupExpr{Expr: me},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return &reNew
|
return &reNew
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalRollupFunc(ec *EvalConfig, name string, rf rollupFunc, re *promql.RollupExpr, iafc *incrementalAggrFuncContext) ([]*timeseries, error) {
|
func evalRollupFunc(ec *EvalConfig, name string, rf rollupFunc, re *metricsql.RollupExpr, iafc *incrementalAggrFuncContext) ([]*timeseries, error) {
|
||||||
ecNew := ec
|
ecNew := ec
|
||||||
var offset int64
|
var offset int64
|
||||||
if len(re.Offset) > 0 {
|
if len(re.Offset) > 0 {
|
||||||
var err error
|
var err error
|
||||||
offset, err = promql.DurationValue(re.Offset, ec.Step)
|
offset, err = metricsql.DurationValue(re.Offset, ec.Step)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -425,7 +424,7 @@ func evalRollupFunc(ec *EvalConfig, name string, rf rollupFunc, re *promql.Rollu
|
||||||
}
|
}
|
||||||
var rvs []*timeseries
|
var rvs []*timeseries
|
||||||
var err error
|
var err error
|
||||||
if me, ok := re.Expr.(*promql.MetricExpr); ok {
|
if me, ok := re.Expr.(*metricsql.MetricExpr); ok {
|
||||||
rvs, err = evalRollupFuncWithMetricExpr(ecNew, name, rf, me, iafc, re.Window)
|
rvs, err = evalRollupFuncWithMetricExpr(ecNew, name, rf, me, iafc, re.Window)
|
||||||
} else {
|
} else {
|
||||||
if iafc != nil {
|
if iafc != nil {
|
||||||
|
@ -450,12 +449,12 @@ func evalRollupFunc(ec *EvalConfig, name string, rf rollupFunc, re *promql.Rollu
|
||||||
return rvs, nil
|
return rvs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalRollupFuncWithSubquery(ec *EvalConfig, name string, rf rollupFunc, re *promql.RollupExpr) ([]*timeseries, error) {
|
func evalRollupFuncWithSubquery(ec *EvalConfig, name string, rf rollupFunc, re *metricsql.RollupExpr) ([]*timeseries, error) {
|
||||||
// Do not use rollupResultCacheV here, since it works only with metricExpr.
|
// Do not use rollupResultCacheV here, since it works only with metricsql.MetricExpr.
|
||||||
var step int64
|
var step int64
|
||||||
if len(re.Step) > 0 {
|
if len(re.Step) > 0 {
|
||||||
var err error
|
var err error
|
||||||
step, err = promql.DurationValue(re.Step, ec.Step)
|
step, err = metricsql.PositiveDurationValue(re.Step, ec.Step)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -465,7 +464,7 @@ func evalRollupFuncWithSubquery(ec *EvalConfig, name string, rf rollupFunc, re *
|
||||||
var window int64
|
var window int64
|
||||||
if len(re.Window) > 0 {
|
if len(re.Window) > 0 {
|
||||||
var err error
|
var err error
|
||||||
window, err = promql.DurationValue(re.Window, ec.Step)
|
window, err = metricsql.PositiveDurationValue(re.Window, ec.Step)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -559,14 +558,14 @@ var (
|
||||||
rollupResultCacheMiss = metrics.NewCounter(`vm_rollup_result_cache_miss_total`)
|
rollupResultCacheMiss = metrics.NewCounter(`vm_rollup_result_cache_miss_total`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func evalRollupFuncWithMetricExpr(ec *EvalConfig, name string, rf rollupFunc, me *promql.MetricExpr, iafc *incrementalAggrFuncContext, windowStr string) ([]*timeseries, error) {
|
func evalRollupFuncWithMetricExpr(ec *EvalConfig, name string, rf rollupFunc, me *metricsql.MetricExpr, iafc *incrementalAggrFuncContext, windowStr string) ([]*timeseries, error) {
|
||||||
if me.IsEmpty() {
|
if me.IsEmpty() {
|
||||||
return evalNumber(ec, nan), nil
|
return evalNumber(ec, nan), nil
|
||||||
}
|
}
|
||||||
var window int64
|
var window int64
|
||||||
if len(windowStr) > 0 {
|
if len(windowStr) > 0 {
|
||||||
var err error
|
var err error
|
||||||
window, err = promql.DurationValue(windowStr, ec.Step)
|
window, err = metricsql.PositiveDurationValue(windowStr, ec.Step)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -586,12 +585,11 @@ func evalRollupFuncWithMetricExpr(ec *EvalConfig, name string, rf rollupFunc, me
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch the remaining part of the result.
|
// Fetch the remaining part of the result.
|
||||||
|
tfs := toTagFilters(me.LabelFilters)
|
||||||
sq := &storage.SearchQuery{
|
sq := &storage.SearchQuery{
|
||||||
MinTimestamp: start - window - maxSilenceInterval,
|
MinTimestamp: start - window - maxSilenceInterval,
|
||||||
MaxTimestamp: ec.End + ec.Step,
|
MaxTimestamp: ec.End + ec.Step,
|
||||||
TagFilterss: [][]storage.TagFilter{
|
TagFilterss: [][]storage.TagFilter{tfs},
|
||||||
*(*[]storage.TagFilter)(unsafe.Pointer(&me.TagFilters)),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
rss, err := netstorage.ProcessSearchQuery(sq, true, ec.Deadline)
|
rss, err := netstorage.ProcessSearchQuery(sq, true, ec.Deadline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -815,3 +813,23 @@ func mulNoOverflow(a, b int64) int64 {
|
||||||
}
|
}
|
||||||
return a * b
|
return a * b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func toTagFilters(lfs []metricsql.LabelFilter) []storage.TagFilter {
|
||||||
|
tfs := make([]storage.TagFilter, len(lfs))
|
||||||
|
for i := range lfs {
|
||||||
|
toTagFilter(&tfs[i], &lfs[i])
|
||||||
|
}
|
||||||
|
return tfs
|
||||||
|
}
|
||||||
|
|
||||||
|
func toTagFilter(dst *storage.TagFilter, src *metricsql.LabelFilter) {
|
||||||
|
if src.Label != "__name__" {
|
||||||
|
dst.Key = []byte(src.Label)
|
||||||
|
} else {
|
||||||
|
// This is required for storage.Search.
|
||||||
|
dst.Key = nil
|
||||||
|
}
|
||||||
|
dst.Value = []byte(src.Value)
|
||||||
|
dst.IsRegexp = src.IsRegexp
|
||||||
|
dst.IsNegative = src.IsNegative
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
"github.com/VictoriaMetrics/metrics"
|
"github.com/VictoriaMetrics/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -86,12 +86,12 @@ func Exec(ec *EvalConfig, q string, isFirstPointOnly bool) ([]netstorage.Result,
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func maySortResults(e promql.Expr, tss []*timeseries) bool {
|
func maySortResults(e metricsql.Expr, tss []*timeseries) bool {
|
||||||
if len(tss) > 100 {
|
if len(tss) > 100 {
|
||||||
// There is no sense in sorting a lot of results
|
// There is no sense in sorting a lot of results
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
fe, ok := e.(*promql.FuncExpr)
|
fe, ok := e.(*metricsql.FuncExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -155,19 +155,10 @@ func removeNaNs(tss []*timeseries) []*timeseries {
|
||||||
return rvs
|
return rvs
|
||||||
}
|
}
|
||||||
|
|
||||||
func parsePromQL(q string) (promql.Expr, error) {
|
func parsePromQLWithCache(q string) (metricsql.Expr, error) {
|
||||||
e, err := parser.ParsePromQL(q)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return simplifyConstants(e), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parsePromQLWithCache(q string) (promql.Expr, error) {
|
|
||||||
pcv := parseCacheV.Get(q)
|
pcv := parseCacheV.Get(q)
|
||||||
if pcv == nil {
|
if pcv == nil {
|
||||||
e, err := parsePromQL(q)
|
e, err := metricsql.Parse(q)
|
||||||
pcv = &parseCacheValue{
|
pcv = &parseCacheValue{
|
||||||
e: e,
|
e: e,
|
||||||
err: err,
|
err: err,
|
||||||
|
@ -180,8 +171,6 @@ func parsePromQLWithCache(q string) (promql.Expr, error) {
|
||||||
return pcv.e, nil
|
return pcv.e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var parser = promql.NewParser(compileRegexpAnchored)
|
|
||||||
|
|
||||||
var parseCacheV = func() *parseCache {
|
var parseCacheV = func() *parseCache {
|
||||||
pc := &parseCache{
|
pc := &parseCache{
|
||||||
m: make(map[string]*parseCacheValue),
|
m: make(map[string]*parseCacheValue),
|
||||||
|
@ -201,7 +190,7 @@ var parseCacheV = func() *parseCache {
|
||||||
const parseCacheMaxLen = 10e3
|
const parseCacheMaxLen = 10e3
|
||||||
|
|
||||||
type parseCacheValue struct {
|
type parseCacheValue struct {
|
||||||
e promql.Expr
|
e metricsql.Expr
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +250,3 @@ func (pc *parseCache) Put(q string, pcv *parseCacheValue) {
|
||||||
pc.m[q] = pcv
|
pc.m[q] = pcv
|
||||||
pc.mu.Unlock()
|
pc.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
promql.Panicf = logger.Panicf
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,12 +2,27 @@ package promql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// IsRollup verifies whether s is a rollup with non-empty window.
|
||||||
|
//
|
||||||
|
// It returns the wrapped query with the corresponding window, step and offset.
|
||||||
|
func IsRollup(s string) (childQuery string, window, step, offset string) {
|
||||||
|
expr, err := parsePromQLWithCache(s)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
re, ok := expr.(*metricsql.RollupExpr)
|
||||||
|
if !ok || len(re.Window) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wrappedQuery := re.Expr.AppendString(nil)
|
||||||
|
return string(wrappedQuery), re.Window, re.Step, re.Offset
|
||||||
|
}
|
||||||
|
|
||||||
// IsMetricSelectorWithRollup verifies whether s contains PromQL metric selector
|
// IsMetricSelectorWithRollup verifies whether s contains PromQL metric selector
|
||||||
// wrapped into rollup.
|
// wrapped into rollup.
|
||||||
//
|
//
|
||||||
|
@ -17,12 +32,12 @@ func IsMetricSelectorWithRollup(s string) (childQuery string, window, offset str
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
re, ok := expr.(*promql.RollupExpr)
|
re, ok := expr.(*metricsql.RollupExpr)
|
||||||
if !ok || len(re.Window) == 0 || len(re.Step) > 0 {
|
if !ok || len(re.Window) == 0 || len(re.Step) > 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
me, ok := re.Expr.(*promql.MetricExpr)
|
me, ok := re.Expr.(*metricsql.MetricExpr)
|
||||||
if !ok || len(me.TagFilters) == 0 {
|
if !ok || len(me.LabelFilters) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
wrappedQuery := me.AppendString(nil)
|
wrappedQuery := me.AppendString(nil)
|
||||||
|
@ -30,18 +45,19 @@ func IsMetricSelectorWithRollup(s string) (childQuery string, window, offset str
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseMetricSelector parses s containing PromQL metric selector
|
// ParseMetricSelector parses s containing PromQL metric selector
|
||||||
// and returns the corresponding TagFilters.
|
// and returns the corresponding LabelFilters.
|
||||||
func ParseMetricSelector(s string) ([]storage.TagFilter, error) {
|
func ParseMetricSelector(s string) ([]storage.TagFilter, error) {
|
||||||
expr, err := parsePromQLWithCache(s)
|
expr, err := parsePromQLWithCache(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
me, ok := expr.(*promql.MetricExpr)
|
me, ok := expr.(*metricsql.MetricExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("expecting metricSelector; got %q", expr.AppendString(nil))
|
return nil, fmt.Errorf("expecting metricSelector; got %q", expr.AppendString(nil))
|
||||||
}
|
}
|
||||||
if len(me.TagFilters) == 0 {
|
if len(me.LabelFilters) == 0 {
|
||||||
return nil, fmt.Errorf("tagFilters cannot be empty")
|
return nil, fmt.Errorf("labelFilters cannot be empty")
|
||||||
}
|
}
|
||||||
return *(*[]storage.TagFilter)(unsafe.Pointer(&me.TagFilters)), nil
|
tfs := toTagFilters(me.LabelFilters)
|
||||||
|
return tfs, nil
|
||||||
}
|
}
|
|
@ -12,8 +12,7 @@ import (
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/encoding"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
||||||
"github.com/VictoriaMetrics/fastcache"
|
"github.com/VictoriaMetrics/fastcache"
|
||||||
"github.com/VictoriaMetrics/metrics"
|
"github.com/VictoriaMetrics/metrics"
|
||||||
|
@ -130,7 +129,7 @@ func ResetRollupResultCache() {
|
||||||
rollupResultCacheV.c.Reset()
|
rollupResultCacheV.c.Reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rrc *rollupResultCache) Get(funcName string, ec *EvalConfig, me *promql.MetricExpr, iafc *incrementalAggrFuncContext, window int64) (tss []*timeseries, newStart int64) {
|
func (rrc *rollupResultCache) Get(funcName string, ec *EvalConfig, me *metricsql.MetricExpr, iafc *incrementalAggrFuncContext, window int64) (tss []*timeseries, newStart int64) {
|
||||||
if *disableCache || !ec.mayCache() {
|
if *disableCache || !ec.mayCache() {
|
||||||
return nil, ec.Start
|
return nil, ec.Start
|
||||||
}
|
}
|
||||||
|
@ -211,7 +210,7 @@ func (rrc *rollupResultCache) Get(funcName string, ec *EvalConfig, me *promql.Me
|
||||||
|
|
||||||
var resultBufPool bytesutil.ByteBufferPool
|
var resultBufPool bytesutil.ByteBufferPool
|
||||||
|
|
||||||
func (rrc *rollupResultCache) Put(funcName string, ec *EvalConfig, me *promql.MetricExpr, iafc *incrementalAggrFuncContext, window int64, tss []*timeseries) {
|
func (rrc *rollupResultCache) Put(funcName string, ec *EvalConfig, me *metricsql.MetricExpr, iafc *incrementalAggrFuncContext, window int64, tss []*timeseries) {
|
||||||
if *disableCache || len(tss) == 0 || !ec.mayCache() {
|
if *disableCache || len(tss) == 0 || !ec.mayCache() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -292,7 +291,7 @@ var tooBigRollupResults = metrics.NewCounter("vm_too_big_rollup_results_total")
|
||||||
// Increment this value every time the format of the cache changes.
|
// Increment this value every time the format of the cache changes.
|
||||||
const rollupResultCacheVersion = 6
|
const rollupResultCacheVersion = 6
|
||||||
|
|
||||||
func marshalRollupResultCacheKey(dst []byte, funcName string, me *promql.MetricExpr, iafc *incrementalAggrFuncContext, window, step int64) []byte {
|
func marshalRollupResultCacheKey(dst []byte, funcName string, me *metricsql.MetricExpr, iafc *incrementalAggrFuncContext, window, step int64) []byte {
|
||||||
dst = append(dst, rollupResultCacheVersion)
|
dst = append(dst, rollupResultCacheVersion)
|
||||||
if iafc == nil {
|
if iafc == nil {
|
||||||
dst = append(dst, 0)
|
dst = append(dst, 0)
|
||||||
|
@ -304,9 +303,9 @@ func marshalRollupResultCacheKey(dst []byte, funcName string, me *promql.MetricE
|
||||||
dst = append(dst, funcName...)
|
dst = append(dst, funcName...)
|
||||||
dst = encoding.MarshalInt64(dst, window)
|
dst = encoding.MarshalInt64(dst, window)
|
||||||
dst = encoding.MarshalInt64(dst, step)
|
dst = encoding.MarshalInt64(dst, step)
|
||||||
for i := range me.TagFilters {
|
tfs := toTagFilters(me.LabelFilters)
|
||||||
stf := storage.TagFilter(me.TagFilters[i])
|
for i := range tfs {
|
||||||
dst = stf.Marshal(dst)
|
dst = tfs[i].Marshal(dst)
|
||||||
}
|
}
|
||||||
return dst
|
return dst
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ package promql
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,14 +18,14 @@ func TestRollupResultCache(t *testing.T) {
|
||||||
|
|
||||||
MayCache: true,
|
MayCache: true,
|
||||||
}
|
}
|
||||||
me := &promql.MetricExpr{
|
me := &metricsql.MetricExpr{
|
||||||
TagFilters: []promql.TagFilter{{
|
LabelFilters: []metricsql.LabelFilter{{
|
||||||
Key: []byte("aaa"),
|
Label: "aaa",
|
||||||
Value: []byte("xxx"),
|
Value: "xxx",
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
iafc := &incrementalAggrFuncContext{
|
iafc := &incrementalAggrFuncContext{
|
||||||
ae: &promql.AggrFuncExpr{
|
ae: &metricsql.AggrFuncExpr{
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package promql
|
package promql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
|
||||||
"math"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -158,7 +159,7 @@ func TestDerivValues(t *testing.T) {
|
||||||
testRowsEqual(t, values, timestamps, valuesExpected, timestamps)
|
testRowsEqual(t, values, timestamps, valuesExpected, timestamps)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testRollupFunc(t *testing.T, funcName string, args []interface{}, meExpected *promql.MetricExpr, vExpected float64) {
|
func testRollupFunc(t *testing.T, funcName string, args []interface{}, meExpected *metricsql.MetricExpr, vExpected float64) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
nrf := getRollupFunc(funcName)
|
nrf := getRollupFunc(funcName)
|
||||||
if nrf == nil {
|
if nrf == nil {
|
||||||
|
@ -198,8 +199,8 @@ func TestRollupQuantileOverTime(t *testing.T) {
|
||||||
Values: []float64{phi},
|
Values: []float64{phi},
|
||||||
Timestamps: []int64{123},
|
Timestamps: []int64{123},
|
||||||
}}
|
}}
|
||||||
var me promql.MetricExpr
|
var me metricsql.MetricExpr
|
||||||
args := []interface{}{phis, &promql.RollupExpr{Expr: &me}}
|
args := []interface{}{phis, &metricsql.RollupExpr{Expr: &me}}
|
||||||
testRollupFunc(t, "quantile_over_time", args, &me, vExpected)
|
testRollupFunc(t, "quantile_over_time", args, &me, vExpected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,8 +221,8 @@ func TestRollupPredictLinear(t *testing.T) {
|
||||||
Values: []float64{sec},
|
Values: []float64{sec},
|
||||||
Timestamps: []int64{123},
|
Timestamps: []int64{123},
|
||||||
}}
|
}}
|
||||||
var me promql.MetricExpr
|
var me metricsql.MetricExpr
|
||||||
args := []interface{}{&promql.RollupExpr{Expr: &me}, secs}
|
args := []interface{}{&metricsql.RollupExpr{Expr: &me}, secs}
|
||||||
testRollupFunc(t, "predict_linear", args, &me, vExpected)
|
testRollupFunc(t, "predict_linear", args, &me, vExpected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,8 +243,8 @@ func TestRollupHoltWinters(t *testing.T) {
|
||||||
Values: []float64{tf},
|
Values: []float64{tf},
|
||||||
Timestamps: []int64{123},
|
Timestamps: []int64{123},
|
||||||
}}
|
}}
|
||||||
var me promql.MetricExpr
|
var me metricsql.MetricExpr
|
||||||
args := []interface{}{&promql.RollupExpr{Expr: &me}, sfs, tfs}
|
args := []interface{}{&metricsql.RollupExpr{Expr: &me}, sfs, tfs}
|
||||||
testRollupFunc(t, "holt_winters", args, &me, vExpected)
|
testRollupFunc(t, "holt_winters", args, &me, vExpected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,8 +267,8 @@ func TestRollupHoltWinters(t *testing.T) {
|
||||||
func TestRollupNewRollupFuncSuccess(t *testing.T) {
|
func TestRollupNewRollupFuncSuccess(t *testing.T) {
|
||||||
f := func(funcName string, vExpected float64) {
|
f := func(funcName string, vExpected float64) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
var me promql.MetricExpr
|
var me metricsql.MetricExpr
|
||||||
args := []interface{}{&promql.RollupExpr{Expr: &me}}
|
args := []interface{}{&metricsql.RollupExpr{Expr: &me}}
|
||||||
testRollupFunc(t, funcName, args, &me, vExpected)
|
testRollupFunc(t, funcName, args, &me, vExpected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +329,7 @@ func TestRollupNewRollupFuncError(t *testing.T) {
|
||||||
Values: []float64{321},
|
Values: []float64{321},
|
||||||
Timestamps: []int64{123},
|
Timestamps: []int64{123},
|
||||||
}}
|
}}
|
||||||
me := &promql.MetricExpr{}
|
me := &metricsql.MetricExpr{}
|
||||||
f("holt_winters", []interface{}{123, 123, 321})
|
f("holt_winters", []interface{}{123, 123, 321})
|
||||||
f("holt_winters", []interface{}{me, 123, 321})
|
f("holt_winters", []interface{}{me, 123, 321})
|
||||||
f("holt_winters", []interface{}{me, scalarTs, 321})
|
f("holt_winters", []interface{}{me, scalarTs, 321})
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
package promql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
|
||||||
)
|
|
||||||
|
|
||||||
func simplifyConstants(e promql.Expr) promql.Expr {
|
|
||||||
if re, ok := e.(*promql.RollupExpr); ok {
|
|
||||||
re.Expr = simplifyConstants(re.Expr)
|
|
||||||
return re
|
|
||||||
}
|
|
||||||
if ae, ok := e.(*promql.AggrFuncExpr); ok {
|
|
||||||
simplifyConstantsInplace(ae.Args)
|
|
||||||
return ae
|
|
||||||
}
|
|
||||||
if fe, ok := e.(*promql.FuncExpr); ok {
|
|
||||||
simplifyConstantsInplace(fe.Args)
|
|
||||||
return fe
|
|
||||||
}
|
|
||||||
if pe, ok := e.(*promql.ParensExpr); ok {
|
|
||||||
if len(*pe) == 1 {
|
|
||||||
return simplifyConstants((*pe)[0])
|
|
||||||
}
|
|
||||||
simplifyConstantsInplace(*pe)
|
|
||||||
return pe
|
|
||||||
}
|
|
||||||
be, ok := e.(*promql.BinaryOpExpr)
|
|
||||||
if !ok {
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
be.Left = simplifyConstants(be.Left)
|
|
||||||
be.Right = simplifyConstants(be.Right)
|
|
||||||
|
|
||||||
lne, ok := be.Left.(*promql.NumberExpr)
|
|
||||||
if !ok {
|
|
||||||
return be
|
|
||||||
}
|
|
||||||
rne, ok := be.Right.(*promql.NumberExpr)
|
|
||||||
if !ok {
|
|
||||||
return be
|
|
||||||
}
|
|
||||||
n := binaryOpConstants(be.Op, lne.N, rne.N, be.Bool)
|
|
||||||
ne := &promql.NumberExpr{
|
|
||||||
N: n,
|
|
||||||
}
|
|
||||||
return ne
|
|
||||||
}
|
|
||||||
|
|
||||||
func simplifyConstantsInplace(args []promql.Expr) {
|
|
||||||
for i, arg := range args {
|
|
||||||
args[i] = simplifyConstants(arg)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package promql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSimplify(t *testing.T) {
|
|
||||||
another := func(s string, sExpected string) {
|
|
||||||
t.Helper()
|
|
||||||
|
|
||||||
e, err := parsePromQL(s)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error when parsing %q: %s", s, err)
|
|
||||||
}
|
|
||||||
res := e.AppendString(nil)
|
|
||||||
if string(res) != sExpected {
|
|
||||||
t.Fatalf("unexpected string constructed;\ngot\n%q\nwant\n%q", res, sExpected)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simplification cases
|
|
||||||
another(`nan == nan`, `NaN`)
|
|
||||||
another(`nan ==bool nan`, `1`)
|
|
||||||
another(`nan !=bool nan`, `0`)
|
|
||||||
another(`nan !=bool 2`, `1`)
|
|
||||||
another(`2 !=bool nan`, `1`)
|
|
||||||
another(`nan >bool nan`, `0`)
|
|
||||||
another(`nan <bool nan`, `0`)
|
|
||||||
another(`1 ==bool nan`, `0`)
|
|
||||||
another(`NaN !=bool 1`, `1`)
|
|
||||||
another(`inf >=bool 2`, `1`)
|
|
||||||
another(`-1 >bool -inf`, `1`)
|
|
||||||
another(`-1 <bool -inf`, `0`)
|
|
||||||
another(`nan + 2 *3 * inf`, `NaN`)
|
|
||||||
another(`INF - Inf`, `NaN`)
|
|
||||||
another(`Inf + inf`, `+Inf`)
|
|
||||||
another(`1/0`, `+Inf`)
|
|
||||||
another(`0/0`, `NaN`)
|
|
||||||
another(`1 or 2`, `1`)
|
|
||||||
another(`1 and 2`, `1`)
|
|
||||||
another(`1 unless 2`, `NaN`)
|
|
||||||
another(`1 default 2`, `1`)
|
|
||||||
another(`1 default NaN`, `1`)
|
|
||||||
another(`NaN default 2`, `2`)
|
|
||||||
another(`1 > 2`, `NaN`)
|
|
||||||
another(`1 > bool 2`, `0`)
|
|
||||||
another(`3 >= 2`, `3`)
|
|
||||||
another(`3 <= bool 2`, `0`)
|
|
||||||
another(`1 + -2 - 3`, `-4`)
|
|
||||||
another(`1 / 0 + 2`, `+Inf`)
|
|
||||||
another(`2 + -1 / 0`, `-Inf`)
|
|
||||||
another(`-1 ^ 0.5`, `NaN`)
|
|
||||||
another(`512.5 - (1 + 3) * (2 ^ 2) ^ 3`, `256.5`)
|
|
||||||
another(`1 == bool 1 != bool 24 < bool 4 > bool -1`, `1`)
|
|
||||||
another(`1 == bOOl 1 != BOOL 24 < Bool 4 > booL -1`, `1`)
|
|
||||||
another(`1+2 if 2>3`, `NaN`)
|
|
||||||
another(`1+4 if 2<3`, `5`)
|
|
||||||
another(`2+6 default 3 if 2>3`, `8`)
|
|
||||||
another(`2+6 if 2>3 default NaN`, `NaN`)
|
|
||||||
another(`42 if 3>2 if 2+2<5`, `42`)
|
|
||||||
another(`42 if 3>2 if 2+2>=5`, `NaN`)
|
|
||||||
another(`1+2 ifnot 2>3`, `3`)
|
|
||||||
another(`1+4 ifnot 2<3`, `NaN`)
|
|
||||||
another(`2+6 default 3 ifnot 2>3`, `8`)
|
|
||||||
another(`2+6 ifnot 2>3 default NaN`, `8`)
|
|
||||||
another(`42 if 3>2 ifnot 2+2<5`, `NaN`)
|
|
||||||
another(`42 if 3>2 ifnot 2+2>=5`, `42`)
|
|
||||||
another(`5 - 1 + 3 * 2 ^ 2 ^ 3 - 2 OR Metric {Bar= "Baz", aaa!="bb",cc=~"dd" ,zz !~"ff" } `,
|
|
||||||
`770 or Metric{Bar="Baz", aaa!="bb", cc=~"dd", zz!~"ff"}`)
|
|
||||||
another(`((foo(bar,baz)), (1+(2)+(3,4)+()))`, `(foo(bar, baz), (3 + (3, 4)) + ())`)
|
|
||||||
another(` FOO (bar) + f ( m ( ),ff(1 + ( 2.5)) ,M[5m ] , "ff" )`, `FOO(bar) + f(m(), ff(3.5), M[5m], "ff")`)
|
|
||||||
another(`sum without (a, b) (xx,2+2)`, `sum(xx, 4) without (a, b)`)
|
|
||||||
another(`Sum WIthout (a, B) (XX,2+2)`, `sum(XX, 4) without (a, B)`)
|
|
||||||
another(`with (foo = bar{x="x"}) 1+1`, `2`)
|
|
||||||
another(`with (x(a, b) = a + b) x(foo, x(1, 2))`, `foo + 3`)
|
|
||||||
another(`WITH (
|
|
||||||
x2(x) = x^2,
|
|
||||||
f(x, y) = x2(x) + x*y + x2(y)
|
|
||||||
)
|
|
||||||
f(a, 3)
|
|
||||||
`, `((a ^ 2) + (a * 3)) + 9`)
|
|
||||||
another(`WITH (
|
|
||||||
x2(x) = x^2,
|
|
||||||
f(x, y) = x2(x) + x*y + x2(y)
|
|
||||||
)
|
|
||||||
f(2, 3)
|
|
||||||
`, `19`)
|
|
||||||
another(`with(y=123,z=5) union(with(y=3,f(x)=x*y) f(2) + f(3), with(x=5,y=2) x*y*z)`, `union(15, 50)`)
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/decimal"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/decimal"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
"github.com/valyala/histogram"
|
"github.com/valyala/histogram"
|
||||||
)
|
)
|
||||||
|
@ -102,7 +102,7 @@ func getTransformFunc(s string) transformFunc {
|
||||||
|
|
||||||
type transformFuncArg struct {
|
type transformFuncArg struct {
|
||||||
ec *EvalConfig
|
ec *EvalConfig
|
||||||
fe *promql.FuncExpr
|
fe *metricsql.FuncExpr
|
||||||
args [][]*timeseries
|
args [][]*timeseries
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ func newTransformFuncOneArg(tf func(v float64) float64) transformFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doTransformValues(arg []*timeseries, tf func(values []float64), fe *promql.FuncExpr) ([]*timeseries, error) {
|
func doTransformValues(arg []*timeseries, tf func(values []float64), fe *metricsql.FuncExpr) ([]*timeseries, error) {
|
||||||
name := strings.ToLower(fe.Name)
|
name := strings.ToLower(fe.Name)
|
||||||
keepMetricGroup := transformFuncsKeepMetricGroup[name]
|
keepMetricGroup := transformFuncsKeepMetricGroup[name]
|
||||||
for _, ts := range arg {
|
for _, ts := range arg {
|
||||||
|
@ -151,12 +151,13 @@ func transformAbsent(tfa *transformFuncArg) ([]*timeseries, error) {
|
||||||
// Copy tags from arg
|
// Copy tags from arg
|
||||||
rvs := evalNumber(tfa.ec, 1)
|
rvs := evalNumber(tfa.ec, 1)
|
||||||
rv := rvs[0]
|
rv := rvs[0]
|
||||||
me, ok := tfa.fe.Args[0].(*promql.MetricExpr)
|
me, ok := tfa.fe.Args[0].(*metricsql.MetricExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
return rvs, nil
|
return rvs, nil
|
||||||
}
|
}
|
||||||
for i := range me.TagFilters {
|
tfs := toTagFilters(me.LabelFilters)
|
||||||
tf := &me.TagFilters[i]
|
for i := range tfs {
|
||||||
|
tf := &tfs[i]
|
||||||
if len(tf.Key) == 0 {
|
if len(tf.Key) == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -1020,7 +1021,7 @@ func transformLabelTransform(tfa *transformFuncArg) ([]*timeseries, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := compileRegexp(regex)
|
r, err := metricsql.CompileRegexp(regex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(`cannot compile regex %q: %s`, regex, err)
|
return nil, fmt.Errorf(`cannot compile regex %q: %s`, regex, err)
|
||||||
}
|
}
|
||||||
|
@ -1049,7 +1050,7 @@ func transformLabelReplace(tfa *transformFuncArg) ([]*timeseries, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := compileRegexpAnchored(regex)
|
r, err := metricsql.CompileRegexpAnchored(regex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf(`cannot compile regex %q: %s`, regex, err)
|
return nil, fmt.Errorf(`cannot compile regex %q: %s`, regex, err)
|
||||||
}
|
}
|
||||||
|
@ -1160,7 +1161,7 @@ func transformScalar(tfa *transformFuncArg) ([]*timeseries, error) {
|
||||||
|
|
||||||
// Verify whether the arg is a string.
|
// Verify whether the arg is a string.
|
||||||
// Then try converting the string to number.
|
// Then try converting the string to number.
|
||||||
if se, ok := tfa.fe.Args[0].(*promql.StringExpr); ok {
|
if se, ok := tfa.fe.Args[0].(*metricsql.StringExpr); ok {
|
||||||
n, err := strconv.ParseFloat(se.S, 64)
|
n, err := strconv.ParseFloat(se.S, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
n = nan
|
n = nan
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -18,7 +18,7 @@ var aggrFuncs = map[string]bool{
|
||||||
"topk": true,
|
"topk": true,
|
||||||
"quantile": true,
|
"quantile": true,
|
||||||
|
|
||||||
// Extended PromQL funcs
|
// MetricsQL extension funcs
|
||||||
"median": true,
|
"median": true,
|
||||||
"limitk": true,
|
"limitk": true,
|
||||||
"distinct": true,
|
"distinct": true,
|
|
@ -1,4 +1,4 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
205
lib/metricsql/binary_op.go
Normal file
205
lib/metricsql/binary_op.go
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
package metricsql
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql/binaryop"
|
||||||
|
)
|
||||||
|
|
||||||
|
var binaryOps = map[string]bool{
|
||||||
|
"+": true,
|
||||||
|
"-": true,
|
||||||
|
"*": true,
|
||||||
|
"/": true,
|
||||||
|
"%": true,
|
||||||
|
"^": true,
|
||||||
|
|
||||||
|
// cmp ops
|
||||||
|
"==": true,
|
||||||
|
"!=": true,
|
||||||
|
">": true,
|
||||||
|
"<": true,
|
||||||
|
">=": true,
|
||||||
|
"<=": true,
|
||||||
|
|
||||||
|
// logical set ops
|
||||||
|
"and": true,
|
||||||
|
"or": true,
|
||||||
|
"unless": true,
|
||||||
|
|
||||||
|
// New ops for MetricsQL
|
||||||
|
"if": true,
|
||||||
|
"ifnot": true,
|
||||||
|
"default": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var binaryOpPriorities = map[string]int{
|
||||||
|
"default": -1,
|
||||||
|
|
||||||
|
"if": 0,
|
||||||
|
"ifnot": 0,
|
||||||
|
|
||||||
|
// See https://prometheus.io/docs/prometheus/latest/querying/operators/#binary-operator-precedence
|
||||||
|
"or": 1,
|
||||||
|
|
||||||
|
"and": 2,
|
||||||
|
"unless": 2,
|
||||||
|
|
||||||
|
"==": 3,
|
||||||
|
"!=": 3,
|
||||||
|
"<": 3,
|
||||||
|
">": 3,
|
||||||
|
"<=": 3,
|
||||||
|
">=": 3,
|
||||||
|
|
||||||
|
"+": 4,
|
||||||
|
"-": 4,
|
||||||
|
|
||||||
|
"*": 5,
|
||||||
|
"/": 5,
|
||||||
|
"%": 5,
|
||||||
|
|
||||||
|
"^": 6,
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBinaryOp(op string) bool {
|
||||||
|
op = strings.ToLower(op)
|
||||||
|
return binaryOps[op]
|
||||||
|
}
|
||||||
|
|
||||||
|
func binaryOpPriority(op string) int {
|
||||||
|
op = strings.ToLower(op)
|
||||||
|
return binaryOpPriorities[op]
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanBinaryOpPrefix(s string) int {
|
||||||
|
n := 0
|
||||||
|
for op := range binaryOps {
|
||||||
|
if len(s) < len(op) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ss := strings.ToLower(s[:len(op)])
|
||||||
|
if ss == op && len(op) > n {
|
||||||
|
n = len(op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
|
func isRightAssociativeBinaryOp(op string) bool {
|
||||||
|
// See https://prometheus.io/docs/prometheus/latest/querying/operators/#binary-operator-precedence
|
||||||
|
return op == "^"
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBinaryOpGroupModifier(s string) bool {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
switch s {
|
||||||
|
// See https://prometheus.io/docs/prometheus/latest/querying/operators/#vector-matching
|
||||||
|
case "on", "ignoring":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBinaryOpJoinModifier(s string) bool {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
switch s {
|
||||||
|
case "group_left", "group_right":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBinaryOpBoolModifier(s string) bool {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
return s == "bool"
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsBinaryOpCmp returns true if op is comparison operator such as '==', '!=', etc.
|
||||||
|
func IsBinaryOpCmp(op string) bool {
|
||||||
|
switch op {
|
||||||
|
case "==", "!=", ">", "<", ">=", "<=":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBinaryOpLogicalSet(op string) bool {
|
||||||
|
op = strings.ToLower(op)
|
||||||
|
switch op {
|
||||||
|
case "and", "or", "unless":
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func binaryOpEval(op string, left, right float64, isBool bool) float64 {
|
||||||
|
if IsBinaryOpCmp(op) {
|
||||||
|
evalCmp := func(cf func(left, right float64) bool) float64 {
|
||||||
|
if isBool {
|
||||||
|
if cf(left, right) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if cf(left, right) {
|
||||||
|
return left
|
||||||
|
}
|
||||||
|
return nan
|
||||||
|
}
|
||||||
|
switch op {
|
||||||
|
case "==":
|
||||||
|
left = evalCmp(binaryop.Eq)
|
||||||
|
case "!=":
|
||||||
|
left = evalCmp(binaryop.Neq)
|
||||||
|
case ">":
|
||||||
|
left = evalCmp(binaryop.Gt)
|
||||||
|
case "<":
|
||||||
|
left = evalCmp(binaryop.Lt)
|
||||||
|
case ">=":
|
||||||
|
left = evalCmp(binaryop.Gte)
|
||||||
|
case "<=":
|
||||||
|
left = evalCmp(binaryop.Lte)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("BUG: unexpected comparison binaryOp: %q", op))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch op {
|
||||||
|
case "+":
|
||||||
|
left = binaryop.Plus(left, right)
|
||||||
|
case "-":
|
||||||
|
left = binaryop.Minus(left, right)
|
||||||
|
case "*":
|
||||||
|
left = binaryop.Mul(left, right)
|
||||||
|
case "/":
|
||||||
|
left = binaryop.Div(left, right)
|
||||||
|
case "%":
|
||||||
|
left = binaryop.Mod(left, right)
|
||||||
|
case "^":
|
||||||
|
left = binaryop.Pow(left, right)
|
||||||
|
case "and":
|
||||||
|
// Nothing to do
|
||||||
|
case "or":
|
||||||
|
// Nothing to do
|
||||||
|
case "unless":
|
||||||
|
left = nan
|
||||||
|
case "default":
|
||||||
|
left = binaryop.Default(left, right)
|
||||||
|
case "if":
|
||||||
|
left = binaryop.If(left, right)
|
||||||
|
case "ifnot":
|
||||||
|
left = binaryop.Ifnot(left, right)
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("BUG: unexpected non-comparison binaryOp: %q", op))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return left
|
||||||
|
}
|
||||||
|
|
||||||
|
var nan = math.NaN()
|
|
@ -1,4 +1,4 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
104
lib/metricsql/binaryop/funcs.go
Normal file
104
lib/metricsql/binaryop/funcs.go
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
package binaryop
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
var nan = math.NaN()
|
||||||
|
|
||||||
|
// Eq returns true of left == right.
|
||||||
|
func Eq(left, right float64) bool {
|
||||||
|
// Special handling for nan == nan.
|
||||||
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/150 .
|
||||||
|
if math.IsNaN(left) {
|
||||||
|
return math.IsNaN(right)
|
||||||
|
}
|
||||||
|
return left == right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neq returns true of left != right.
|
||||||
|
func Neq(left, right float64) bool {
|
||||||
|
// Special handling for comparison with nan.
|
||||||
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/150 .
|
||||||
|
if math.IsNaN(left) {
|
||||||
|
return !math.IsNaN(right)
|
||||||
|
}
|
||||||
|
if math.IsNaN(right) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return left != right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gt returns true of left > right
|
||||||
|
func Gt(left, right float64) bool {
|
||||||
|
return left > right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lt returns true if left < right
|
||||||
|
func Lt(left, right float64) bool {
|
||||||
|
return left < right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gte returns true if left >= right
|
||||||
|
func Gte(left, right float64) bool {
|
||||||
|
return left >= right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lte returns true if left <= right
|
||||||
|
func Lte(left, right float64) bool {
|
||||||
|
return left <= right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Plus returns left + right
|
||||||
|
func Plus(left, right float64) float64 {
|
||||||
|
return left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minus returns left - right
|
||||||
|
func Minus(left, right float64) float64 {
|
||||||
|
return left - right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mul returns left * right
|
||||||
|
func Mul(left, right float64) float64 {
|
||||||
|
return left * right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Div returns left / right
|
||||||
|
func Div(left, right float64) float64 {
|
||||||
|
return left / right
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mod returns mod(left, right)
|
||||||
|
func Mod(left, right float64) float64 {
|
||||||
|
return math.Mod(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pow returns pow(left, right)
|
||||||
|
func Pow(left, right float64) float64 {
|
||||||
|
return math.Pow(left, right)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default returns left or right if left is NaN.
|
||||||
|
func Default(left, right float64) float64 {
|
||||||
|
if math.IsNaN(left) {
|
||||||
|
return right
|
||||||
|
}
|
||||||
|
return left
|
||||||
|
}
|
||||||
|
|
||||||
|
// If returns left if right is not NaN. Otherwise NaN is returned.
|
||||||
|
func If(left, right float64) float64 {
|
||||||
|
if math.IsNaN(right) {
|
||||||
|
return nan
|
||||||
|
}
|
||||||
|
return left
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ifnot returns left if right is NaN. Otherwise NaN is returned.
|
||||||
|
func Ifnot(left, right float64) float64 {
|
||||||
|
if math.IsNaN(right) {
|
||||||
|
return left
|
||||||
|
}
|
||||||
|
return nan
|
||||||
|
}
|
15
lib/metricsql/doc.go
Normal file
15
lib/metricsql/doc.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
// Package metricsql implements MetricsQL parser.
|
||||||
|
//
|
||||||
|
// This parser can parse PromQL. Additionally it can parse MetricsQL extensions.
|
||||||
|
// See https://github.com/VictoriaMetrics/VictoriaMetrics/wiki/ExtendedPromQL for details about MetricsQL extensions.
|
||||||
|
//
|
||||||
|
// Usage:
|
||||||
|
//
|
||||||
|
// expr, err := metricsql.Parse(`sum(rate(foo{bar="baz"}[5m])) by (job)`)
|
||||||
|
// if err != nil {
|
||||||
|
// // parse error
|
||||||
|
// }
|
||||||
|
// // Now expr contains parsed MetricsQL as `*Expr` structs.
|
||||||
|
// // See metricsql.Parse examples for more details.
|
||||||
|
//
|
||||||
|
package metricsql
|
|
@ -1,4 +1,4 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -220,7 +220,7 @@ func scanIdent(s string) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
Panicf("BUG: scanIdent couldn't find a single ident char; make sure isIdentPrefix called before scanIdent")
|
panic("BUG: scanIdent couldn't find a single ident char; make sure isIdentPrefix called before scanIdent")
|
||||||
}
|
}
|
||||||
return s[:i]
|
return s[:i]
|
||||||
}
|
}
|
||||||
|
@ -279,7 +279,7 @@ func toHex(n byte) byte {
|
||||||
return 'a' + (n - 10)
|
return 'a' + (n - 10)
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendEscapedIdent(dst, s []byte) []byte {
|
func appendEscapedIdent(dst []byte, s string) []byte {
|
||||||
for i := 0; i < len(s); i++ {
|
for i := 0; i < len(s); i++ {
|
||||||
ch := s[i]
|
ch := s[i]
|
||||||
if isIdentChar(ch) {
|
if isIdentChar(ch) {
|
|
@ -1,4 +1,4 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
@ -28,7 +28,7 @@ func TestUnescapeIdent(t *testing.T) {
|
||||||
func TestAppendEscapedIdent(t *testing.T) {
|
func TestAppendEscapedIdent(t *testing.T) {
|
||||||
f := func(s, resultExpected string) {
|
f := func(s, resultExpected string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
result := appendEscapedIdent(nil, []byte(s))
|
result := appendEscapedIdent(nil, s)
|
||||||
if string(result) != resultExpected {
|
if string(result) != resultExpected {
|
||||||
t.Fatalf("unexpected result for appendEscapedIdent(%q); got %q; want %q", s, result, resultExpected)
|
t.Fatalf("unexpected result for appendEscapedIdent(%q); got %q; want %q", s, result, resultExpected)
|
||||||
}
|
}
|
1720
lib/metricsql/parser.go
Normal file
1720
lib/metricsql/parser.go
Normal file
File diff suppressed because it is too large
Load diff
35
lib/metricsql/parser_example_test.go
Normal file
35
lib/metricsql/parser_example_test.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package metricsql_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ExampleParse() {
|
||||||
|
expr, err := metricsql.Parse(`sum(rate(foo{bar="baz"}[5m])) by (x,y)`)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("parse error: %s", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("parsed expr: %s\n", expr.AppendString(nil))
|
||||||
|
|
||||||
|
ae := expr.(*metricsql.AggrFuncExpr)
|
||||||
|
fmt.Printf("aggr func: name=%s, arg=%s, modifier=%s\n", ae.Name, ae.Args[0].AppendString(nil), ae.Modifier.AppendString(nil))
|
||||||
|
|
||||||
|
fe := ae.Args[0].(*metricsql.FuncExpr)
|
||||||
|
fmt.Printf("func: name=%s, arg=%s\n", fe.Name, fe.Args[0].AppendString(nil))
|
||||||
|
|
||||||
|
re := fe.Args[0].(*metricsql.RollupExpr)
|
||||||
|
fmt.Printf("rollup: expr=%s, window=%s\n", re.Expr.AppendString(nil), re.Window)
|
||||||
|
|
||||||
|
me := re.Expr.(*metricsql.MetricExpr)
|
||||||
|
fmt.Printf("metric: labelFilter1=%s, labelFilter2=%s", me.LabelFilters[0].AppendString(nil), me.LabelFilters[1].AppendString(nil))
|
||||||
|
|
||||||
|
// Output:
|
||||||
|
// parsed expr: sum(rate(foo{bar="baz"}[5m])) by (x, y)
|
||||||
|
// aggr func: name=sum, arg=rate(foo{bar="baz"}[5m]), modifier=by (x, y)
|
||||||
|
// func: name=rate, arg=foo{bar="baz"}[5m]
|
||||||
|
// rollup: expr=foo{bar="baz"}, window=5m
|
||||||
|
// metric: labelFilter1=__name__="foo", labelFilter2=bar="baz"
|
||||||
|
}
|
|
@ -1,24 +1,14 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
var testParser = &Parser{
|
func TestParseSuccess(t *testing.T) {
|
||||||
compileRegexpAnchored: compileRegexpAnchored,
|
|
||||||
}
|
|
||||||
|
|
||||||
func compileRegexpAnchored(re string) (*regexp.Regexp, error) {
|
|
||||||
reAnchored := "^(?:" + re + ")$"
|
|
||||||
return regexp.Compile(reAnchored)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParsePromQLSuccess(t *testing.T) {
|
|
||||||
another := func(s string, sExpected string) {
|
another := func(s string, sExpected string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
e, err := testParser.ParsePromQL(s)
|
e, err := Parse(s)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error when parsing %q: %s", s, err)
|
t.Fatalf("unexpected error when parsing %q: %s", s, err)
|
||||||
}
|
}
|
||||||
|
@ -150,24 +140,72 @@ func TestParsePromQLSuccess(t *testing.T) {
|
||||||
another(`-inF`, `-Inf`)
|
another(`-inF`, `-Inf`)
|
||||||
|
|
||||||
// binaryOpExpr
|
// binaryOpExpr
|
||||||
|
another(`nan == nan`, `NaN`)
|
||||||
|
another(`nan ==bool nan`, `1`)
|
||||||
|
another(`nan !=bool nan`, `0`)
|
||||||
|
another(`nan !=bool 2`, `1`)
|
||||||
|
another(`2 !=bool nan`, `1`)
|
||||||
|
another(`nan >bool nan`, `0`)
|
||||||
|
another(`nan <bool nan`, `0`)
|
||||||
|
another(`1 ==bool nan`, `0`)
|
||||||
|
another(`NaN !=bool 1`, `1`)
|
||||||
|
another(`inf >=bool 2`, `1`)
|
||||||
|
another(`-1 >bool -inf`, `1`)
|
||||||
|
another(`-1 <bool -inf`, `0`)
|
||||||
|
another(`nan + 2 *3 * inf`, `NaN`)
|
||||||
|
another(`INF - Inf`, `NaN`)
|
||||||
|
another(`Inf + inf`, `+Inf`)
|
||||||
|
another(`1/0`, `+Inf`)
|
||||||
|
another(`0/0`, `NaN`)
|
||||||
another(`-m`, `0 - m`)
|
another(`-m`, `0 - m`)
|
||||||
same(`m + ignoring () n[5m]`)
|
same(`m + ignoring () n[5m]`)
|
||||||
another(`M + IGNORING () N[5m]`, `M + ignoring () N[5m]`)
|
another(`M + IGNORING () N[5m]`, `M + ignoring () N[5m]`)
|
||||||
same(`m + on (foo) n[5m]`)
|
same(`m + on (foo) n[5m]`)
|
||||||
another(`m + ON (Foo) n[5m]`, `m + on (Foo) n[5m]`)
|
another(`m + ON (Foo) n[5m]`, `m + on (Foo) n[5m]`)
|
||||||
same(`m + ignoring (a, b) n[5m]`)
|
same(`m + ignoring (a, b) n[5m]`)
|
||||||
|
another(`1 or 2`, `1`)
|
||||||
|
another(`1 and 2`, `1`)
|
||||||
|
another(`1 unless 2`, `NaN`)
|
||||||
|
another(`1 default 2`, `1`)
|
||||||
|
another(`1 default NaN`, `1`)
|
||||||
|
another(`NaN default 2`, `2`)
|
||||||
|
another(`1 > 2`, `NaN`)
|
||||||
|
another(`1 > bool 2`, `0`)
|
||||||
|
another(`3 >= 2`, `3`)
|
||||||
|
another(`3 <= bool 2`, `0`)
|
||||||
|
another(`1 + -2 - 3`, `-4`)
|
||||||
|
another(`1 / 0 + 2`, `+Inf`)
|
||||||
|
another(`2 + -1 / 0`, `-Inf`)
|
||||||
|
another(`-1 ^ 0.5`, `NaN`)
|
||||||
|
another(`512.5 - (1 + 3) * (2 ^ 2) ^ 3`, `256.5`)
|
||||||
|
another(`1 == bool 1 != bool 24 < bool 4 > bool -1`, `1`)
|
||||||
|
another(`1 == bOOl 1 != BOOL 24 < Bool 4 > booL -1`, `1`)
|
||||||
another(`m1+on(foo)group_left m2`, `m1 + on (foo) group_left () m2`)
|
another(`m1+on(foo)group_left m2`, `m1 + on (foo) group_left () m2`)
|
||||||
another(`M1+ON(FOO)GROUP_left M2`, `M1 + on (FOO) group_left () M2`)
|
another(`M1+ON(FOO)GROUP_left M2`, `M1 + on (FOO) group_left () M2`)
|
||||||
same(`m1 + on (foo) group_right () m2`)
|
same(`m1 + on (foo) group_right () m2`)
|
||||||
same(`m1 + on (foo, bar) group_right (x, y) m2`)
|
same(`m1 + on (foo, bar) group_right (x, y) m2`)
|
||||||
another(`m1 + on (foo, bar,) group_right (x, y,) m2`, `m1 + on (foo, bar) group_right (x, y) m2`)
|
another(`m1 + on (foo, bar,) group_right (x, y,) m2`, `m1 + on (foo, bar) group_right (x, y) m2`)
|
||||||
same(`m1 == bool on (foo, bar) group_right (x, y) m2`)
|
same(`m1 == bool on (foo, bar) group_right (x, y) m2`)
|
||||||
|
another(`5 - 1 + 3 * 2 ^ 2 ^ 3 - 2 OR Metric {Bar= "Baz", aaa!="bb",cc=~"dd" ,zz !~"ff" } `,
|
||||||
|
`770 or Metric{Bar="Baz", aaa!="bb", cc=~"dd", zz!~"ff"}`)
|
||||||
same(`"foo" + bar()`)
|
same(`"foo" + bar()`)
|
||||||
same(`"foo" + bar{x="y"}`)
|
same(`"foo" + bar{x="y"}`)
|
||||||
same(`("foo"[3s] + bar{x="y"})[5m:3s] offset 10s`)
|
same(`("foo"[3s] + bar{x="y"})[5m:3s] offset 10s`)
|
||||||
same(`("foo"[3s] + bar{x="y"})[5i:3i] offset 10i`)
|
same(`("foo"[3s] + bar{x="y"})[5i:3i] offset 10i`)
|
||||||
same(`bar + "foo" offset 3s`)
|
same(`bar + "foo" offset 3s`)
|
||||||
same(`bar + "foo" offset 3i`)
|
same(`bar + "foo" offset 3i`)
|
||||||
|
another(`1+2 if 2>3`, `NaN`)
|
||||||
|
another(`1+4 if 2<3`, `5`)
|
||||||
|
another(`2+6 default 3 if 2>3`, `8`)
|
||||||
|
another(`2+6 if 2>3 default NaN`, `NaN`)
|
||||||
|
another(`42 if 3>2 if 2+2<5`, `42`)
|
||||||
|
another(`42 if 3>2 if 2+2>=5`, `NaN`)
|
||||||
|
another(`1+2 ifnot 2>3`, `3`)
|
||||||
|
another(`1+4 ifnot 2<3`, `NaN`)
|
||||||
|
another(`2+6 default 3 ifnot 2>3`, `8`)
|
||||||
|
another(`2+6 ifnot 2>3 default NaN`, `8`)
|
||||||
|
another(`42 if 3>2 ifnot 2+2<5`, `NaN`)
|
||||||
|
another(`42 if 3>2 ifnot 2+2>=5`, `42`)
|
||||||
|
|
||||||
// parensExpr
|
// parensExpr
|
||||||
another(`(-foo + ((bar) / (baz))) + ((23))`, `((0 - foo) + (bar / baz)) + 23`)
|
another(`(-foo + ((bar) / (baz))) + ((23))`, `((0 - foo) + (bar / baz)) + 23`)
|
||||||
|
@ -176,6 +214,7 @@ func TestParsePromQLSuccess(t *testing.T) {
|
||||||
another(`((foo, bar),(baz))`, `((foo, bar), baz)`)
|
another(`((foo, bar),(baz))`, `((foo, bar), baz)`)
|
||||||
same(`(foo, (bar, baz), ((x, y), (z, y), xx))`)
|
same(`(foo, (bar, baz), ((x, y), (z, y), xx))`)
|
||||||
another(`1+(foo, bar,)`, `1 + (foo, bar)`)
|
another(`1+(foo, bar,)`, `1 + (foo, bar)`)
|
||||||
|
another(`((foo(bar,baz)), (1+(2)+(3,4)+()))`, `(foo(bar, baz), (3 + (3, 4)) + ())`)
|
||||||
same(`()`)
|
same(`()`)
|
||||||
|
|
||||||
// funcExpr
|
// funcExpr
|
||||||
|
@ -192,7 +231,7 @@ func TestParsePromQLSuccess(t *testing.T) {
|
||||||
same(`F(HttpServerRequest)`)
|
same(`F(HttpServerRequest)`)
|
||||||
same(`f(job, foo)`)
|
same(`f(job, foo)`)
|
||||||
same(`F(Job, Foo)`)
|
same(`F(Job, Foo)`)
|
||||||
|
another(` FOO (bar) + f ( m ( ),ff(1 + ( 2.5)) ,M[5m ] , "ff" )`, `FOO(bar) + f(m(), ff(3.5), M[5m], "ff")`)
|
||||||
// funcName matching keywords
|
// funcName matching keywords
|
||||||
same(`by(2)`)
|
same(`by(2)`)
|
||||||
same(`BY(2)`)
|
same(`BY(2)`)
|
||||||
|
@ -215,6 +254,8 @@ func TestParsePromQLSuccess(t *testing.T) {
|
||||||
another(`sum by () (xx)`, `sum(xx) by ()`)
|
another(`sum by () (xx)`, `sum(xx) by ()`)
|
||||||
another(`sum by (s) (xx)[5s]`, `(sum(xx) by (s))[5s]`)
|
another(`sum by (s) (xx)[5s]`, `(sum(xx) by (s))[5s]`)
|
||||||
another(`SUM BY (ZZ, aa) (XX)`, `sum(XX) by (ZZ, aa)`)
|
another(`SUM BY (ZZ, aa) (XX)`, `sum(XX) by (ZZ, aa)`)
|
||||||
|
another(`sum without (a, b) (xx,2+2)`, `sum(xx, 4) without (a, b)`)
|
||||||
|
another(`Sum WIthout (a, B) (XX,2+2)`, `sum(XX, 4) without (a, B)`)
|
||||||
same(`sum(a) or sum(b)`)
|
same(`sum(a) or sum(b)`)
|
||||||
same(`sum(a) by () or sum(b) without (x, y)`)
|
same(`sum(a) by () or sum(b) without (x, y)`)
|
||||||
same(`sum(a) + sum(b)`)
|
same(`sum(a) + sum(b)`)
|
||||||
|
@ -237,7 +278,7 @@ func TestParsePromQLSuccess(t *testing.T) {
|
||||||
another(`with (foo = bar{x="x"}) "x"`, `"x"`)
|
another(`with (foo = bar{x="x"}) "x"`, `"x"`)
|
||||||
another(`with (f="x") f`, `"x"`)
|
another(`with (f="x") f`, `"x"`)
|
||||||
another(`with (foo = bar{x="x"}) x{x="y"}`, `x{x="y"}`)
|
another(`with (foo = bar{x="x"}) x{x="y"}`, `x{x="y"}`)
|
||||||
another(`with (foo = bar{x="x"}) 2`, `2`)
|
another(`with (foo = bar{x="x"}) 1+1`, `2`)
|
||||||
another(`with (foo = bar{x="x"}) f()`, `f()`)
|
another(`with (foo = bar{x="x"}) f()`, `f()`)
|
||||||
another(`with (foo = bar{x="x"}) sum(x)`, `sum(x)`)
|
another(`with (foo = bar{x="x"}) sum(x)`, `sum(x)`)
|
||||||
another(`with (foo = bar{x="x"}) baz{foo="bar"}`, `baz{foo="bar"}`)
|
another(`with (foo = bar{x="x"}) baz{foo="bar"}`, `baz{foo="bar"}`)
|
||||||
|
@ -270,6 +311,7 @@ func TestParsePromQLSuccess(t *testing.T) {
|
||||||
another(`with (x() = y+1) x`, `y + 1`)
|
another(`with (x() = y+1) x`, `y + 1`)
|
||||||
another(`with (x(foo) = foo+1) x(a)`, `a + 1`)
|
another(`with (x(foo) = foo+1) x(a)`, `a + 1`)
|
||||||
another(`with (x(a, b) = a + b) x(foo, bar)`, `foo + bar`)
|
another(`with (x(a, b) = a + b) x(foo, bar)`, `foo + bar`)
|
||||||
|
another(`with (x(a, b) = a + b) x(foo, x(1, 2))`, `foo + 3`)
|
||||||
another(`with (x(a) = sum(a) by (b)) x(xx) / x(y)`, `sum(xx) by (b) / sum(y) by (b)`)
|
another(`with (x(a) = sum(a) by (b)) x(xx) / x(y)`, `sum(xx) by (b) / sum(y) by (b)`)
|
||||||
another(`with (f(a,f,x)=ff(x,f,a)) f(f(x,y,z),1,2)`, `ff(2, 1, ff(z, y, x))`)
|
another(`with (f(a,f,x)=ff(x,f,a)) f(f(x,y,z),1,2)`, `ff(2, 1, ff(z, y, x))`)
|
||||||
another(`with (f(x)=1+f(x)) f(foo{bar="baz"})`, `1 + f(foo{bar="baz"})`)
|
another(`with (f(x)=1+f(x)) f(foo{bar="baz"})`, `1 + f(foo{bar="baz"})`)
|
||||||
|
@ -328,6 +370,18 @@ func TestParsePromQLSuccess(t *testing.T) {
|
||||||
)
|
)
|
||||||
hitRatio < treshold`,
|
hitRatio < treshold`,
|
||||||
`(sum(rate(cache{type="hit", job="cacher", instance=~"1.2.3.4"}[5m])) by (instance) / sum(rate(cache{type="hit", job="cacher", instance=~"1.2.3.4"}[5m]) + rate(cache{type="miss", job="cacher", instance=~"1.2.3.4"}[5m])) by (instance)) < 0.9`)
|
`(sum(rate(cache{type="hit", job="cacher", instance=~"1.2.3.4"}[5m])) by (instance) / sum(rate(cache{type="hit", job="cacher", instance=~"1.2.3.4"}[5m]) + rate(cache{type="miss", job="cacher", instance=~"1.2.3.4"}[5m])) by (instance)) < 0.9`)
|
||||||
|
another(`WITH (
|
||||||
|
x2(x) = x^2,
|
||||||
|
f(x, y) = x2(x) + x*y + x2(y)
|
||||||
|
)
|
||||||
|
f(a, 3)
|
||||||
|
`, `((a ^ 2) + (a * 3)) + 9`)
|
||||||
|
another(`WITH (
|
||||||
|
x2(x) = x^2,
|
||||||
|
f(x, y) = x2(x) + x*y + x2(y)
|
||||||
|
)
|
||||||
|
f(2, 3)
|
||||||
|
`, `19`)
|
||||||
another(`WITH (
|
another(`WITH (
|
||||||
commonFilters = {instance="foo"},
|
commonFilters = {instance="foo"},
|
||||||
timeToFuckup(currv, maxv) = (maxv - currv) / rate(currv)
|
timeToFuckup(currv, maxv) = (maxv - currv) / rate(currv)
|
||||||
|
@ -341,15 +395,16 @@ func TestParsePromQLSuccess(t *testing.T) {
|
||||||
)
|
)
|
||||||
hitRate(cacheHits, cacheMisses)`,
|
hitRate(cacheHits, cacheMisses)`,
|
||||||
`sum(rate(cacheHits{job="foo", instance="bar"})) by (job, instance) / (sum(rate(cacheHits{job="foo", instance="bar"})) by (job, instance) + sum(rate(cacheMisses{job="foo", instance="bar"})) by (job, instance))`)
|
`sum(rate(cacheHits{job="foo", instance="bar"})) by (job, instance) / (sum(rate(cacheHits{job="foo", instance="bar"})) by (job, instance) + sum(rate(cacheMisses{job="foo", instance="bar"})) by (job, instance))`)
|
||||||
|
another(`with(y=123,z=5) union(with(y=3,f(x)=x*y) f(2) + f(3), with(x=5,y=2) x*y*z)`, `union(15, 50)`)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParsePromQLError(t *testing.T) {
|
func TestParseError(t *testing.T) {
|
||||||
f := func(s string) {
|
f := func(s string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
e, err := testParser.ParsePromQL(s)
|
e, err := Parse(s)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Fatalf("expecting non-nil error when parsing %q (expr=%v)", s, e)
|
t.Fatalf("expecting non-nil error when parsing %q", s)
|
||||||
}
|
}
|
||||||
if e != nil {
|
if e != nil {
|
||||||
t.Fatalf("expecting nil expr when parsing %q", s)
|
t.Fatalf("expecting nil expr when parsing %q", s)
|
|
@ -1,4 +1,4 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -8,12 +8,14 @@ import (
|
||||||
"github.com/VictoriaMetrics/metrics"
|
"github.com/VictoriaMetrics/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
func compileRegexpAnchored(re string) (*regexp.Regexp, error) {
|
// CompileRegexpAnchored returns compiled regexp `^re$`.
|
||||||
|
func CompileRegexpAnchored(re string) (*regexp.Regexp, error) {
|
||||||
reAnchored := "^(?:" + re + ")$"
|
reAnchored := "^(?:" + re + ")$"
|
||||||
return compileRegexp(reAnchored)
|
return CompileRegexp(reAnchored)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileRegexp(re string) (*regexp.Regexp, error) {
|
// CompileRegexp returns compile regexp re.
|
||||||
|
func CompileRegexp(re string) (*regexp.Regexp, error) {
|
||||||
rcv := regexpCacheV.Get(re)
|
rcv := regexpCacheV.Get(re)
|
||||||
if rcv != nil {
|
if rcv != nil {
|
||||||
return rcv.r, rcv.err
|
return rcv.r, rcv.err
|
|
@ -1,12 +1,10 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rollupFuncs = map[string]bool{
|
var rollupFuncs = map[string]bool{
|
||||||
"default_rollup": true, // default rollup func
|
|
||||||
|
|
||||||
// Standard rollup funcs from PromQL.
|
// Standard rollup funcs from PromQL.
|
||||||
// See funcs accepting range-vector on https://prometheus.io/docs/prometheus/latest/querying/functions/ .
|
// See funcs accepting range-vector on https://prometheus.io/docs/prometheus/latest/querying/functions/ .
|
||||||
"changes": true,
|
"changes": true,
|
||||||
|
@ -30,6 +28,7 @@ var rollupFuncs = map[string]bool{
|
||||||
"stdvar_over_time": true,
|
"stdvar_over_time": true,
|
||||||
|
|
||||||
// Additional rollup funcs.
|
// Additional rollup funcs.
|
||||||
|
"default_rollup": true,
|
||||||
"sum2_over_time": true,
|
"sum2_over_time": true,
|
||||||
"geomean_over_time": true,
|
"geomean_over_time": true,
|
||||||
"first_over_time": true,
|
"first_over_time": true,
|
|
@ -1,4 +1,4 @@
|
||||||
package promql
|
package metricsql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -36,7 +36,7 @@ var transformFuncs = map[string]bool{
|
||||||
"vector": true,
|
"vector": true,
|
||||||
"year": true,
|
"year": true,
|
||||||
|
|
||||||
// New funcs
|
// New funcs from MetricsQL
|
||||||
"label_set": true,
|
"label_set": true,
|
||||||
"label_del": true,
|
"label_del": true,
|
||||||
"label_keep": true,
|
"label_keep": true,
|
||||||
|
@ -45,7 +45,7 @@ var transformFuncs = map[string]bool{
|
||||||
"label_transform": true,
|
"label_transform": true,
|
||||||
"label_value": true,
|
"label_value": true,
|
||||||
"union": true,
|
"union": true,
|
||||||
"": true,
|
"": true, // empty func is a synonim to union
|
||||||
"keep_last_value": true,
|
"keep_last_value": true,
|
||||||
"start": true,
|
"start": true,
|
||||||
"end": true,
|
"end": true,
|
|
@ -1,109 +0,0 @@
|
||||||
package promql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var binaryOpPriorities = map[string]int{
|
|
||||||
"default": -1,
|
|
||||||
|
|
||||||
"if": 0,
|
|
||||||
"ifnot": 0,
|
|
||||||
|
|
||||||
// See https://prometheus.io/docs/prometheus/latest/querying/operators/#binary-operator-precedence
|
|
||||||
"or": 1,
|
|
||||||
|
|
||||||
"and": 2,
|
|
||||||
"unless": 2,
|
|
||||||
|
|
||||||
"==": 3,
|
|
||||||
"!=": 3,
|
|
||||||
"<": 3,
|
|
||||||
">": 3,
|
|
||||||
"<=": 3,
|
|
||||||
">=": 3,
|
|
||||||
|
|
||||||
"+": 4,
|
|
||||||
"-": 4,
|
|
||||||
|
|
||||||
"*": 5,
|
|
||||||
"/": 5,
|
|
||||||
"%": 5,
|
|
||||||
|
|
||||||
"^": 6,
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBinaryOp(op string) bool {
|
|
||||||
op = strings.ToLower(op)
|
|
||||||
_, ok := binaryOpPriorities[op]
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
func binaryOpPriority(op string) int {
|
|
||||||
op = strings.ToLower(op)
|
|
||||||
return binaryOpPriorities[op]
|
|
||||||
}
|
|
||||||
|
|
||||||
func scanBinaryOpPrefix(s string) int {
|
|
||||||
n := 0
|
|
||||||
for op := range binaryOpPriorities {
|
|
||||||
if len(s) < len(op) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
ss := strings.ToLower(s[:len(op)])
|
|
||||||
if ss == op && len(op) > n {
|
|
||||||
n = len(op)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
|
|
||||||
func isRightAssociativeBinaryOp(op string) bool {
|
|
||||||
// See https://prometheus.io/docs/prometheus/latest/querying/operators/#binary-operator-precedence
|
|
||||||
return op == "^"
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBinaryOpGroupModifier(s string) bool {
|
|
||||||
s = strings.ToLower(s)
|
|
||||||
switch s {
|
|
||||||
// See https://prometheus.io/docs/prometheus/latest/querying/operators/#vector-matching
|
|
||||||
case "on", "ignoring":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBinaryOpJoinModifier(s string) bool {
|
|
||||||
s = strings.ToLower(s)
|
|
||||||
switch s {
|
|
||||||
case "group_left", "group_right":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBinaryOpBoolModifier(s string) bool {
|
|
||||||
s = strings.ToLower(s)
|
|
||||||
return s == "bool"
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBinaryOpCmp(op string) bool {
|
|
||||||
switch op {
|
|
||||||
case "==", "!=", ">", "<", ">=", "<=":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isBinaryOpLogicalSet(op string) bool {
|
|
||||||
op = strings.ToLower(op)
|
|
||||||
switch op {
|
|
||||||
case "and", "or", "unless":
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,423 +0,0 @@
|
||||||
package promql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
)
|
|
||||||
|
|
||||||
// An Expr represents a parsed Extended PromQL expression
|
|
||||||
type Expr interface {
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
AppendString(dst []byte) []byte
|
|
||||||
}
|
|
||||||
|
|
||||||
// A StringExpr represents a string
|
|
||||||
type StringExpr struct {
|
|
||||||
S string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (se *StringExpr) AppendString(dst []byte) []byte {
|
|
||||||
return strconv.AppendQuote(dst, se.S)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A StringTemplateExpr represents a string prior to applying a With clause
|
|
||||||
type StringTemplateExpr struct {
|
|
||||||
// Composite string has non-empty tokens.
|
|
||||||
Tokens []StringToken
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (ste *StringTemplateExpr) AppendString(dst []byte) []byte {
|
|
||||||
if ste == nil {
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
for i, tok := range ste.Tokens {
|
|
||||||
if i > 0 {
|
|
||||||
dst = append(dst, " + "...)
|
|
||||||
}
|
|
||||||
dst = tok.AppendString(dst)
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// A StringToken represents a portion of a string expression
|
|
||||||
type StringToken struct {
|
|
||||||
Ident bool
|
|
||||||
S string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of st to dst.
|
|
||||||
func (st *StringToken) AppendString(dst []byte) []byte {
|
|
||||||
if st.Ident {
|
|
||||||
return appendEscapedIdent(dst, []byte(st.S))
|
|
||||||
}
|
|
||||||
return strconv.AppendQuote(dst, st.S)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A NumberExpr represents a number
|
|
||||||
type NumberExpr struct {
|
|
||||||
N float64
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (ne *NumberExpr) AppendString(dst []byte) []byte {
|
|
||||||
return strconv.AppendFloat(dst, ne.N, 'g', -1, 64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A ParensExpr represents a parens expression
|
|
||||||
type ParensExpr []Expr
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (pe ParensExpr) AppendString(dst []byte) []byte {
|
|
||||||
return appendStringArgListExpr(dst, pe)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A BinaryOpExpr represents a binary operator
|
|
||||||
type BinaryOpExpr struct {
|
|
||||||
Op string
|
|
||||||
|
|
||||||
Bool bool
|
|
||||||
GroupModifier ModifierExpr
|
|
||||||
JoinModifier ModifierExpr
|
|
||||||
|
|
||||||
Left Expr
|
|
||||||
Right Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (be *BinaryOpExpr) AppendString(dst []byte) []byte {
|
|
||||||
if _, ok := be.Left.(*BinaryOpExpr); ok {
|
|
||||||
dst = append(dst, '(')
|
|
||||||
dst = be.Left.AppendString(dst)
|
|
||||||
dst = append(dst, ')')
|
|
||||||
} else {
|
|
||||||
dst = be.Left.AppendString(dst)
|
|
||||||
}
|
|
||||||
dst = append(dst, ' ')
|
|
||||||
dst = append(dst, be.Op...)
|
|
||||||
if be.Bool {
|
|
||||||
dst = append(dst, " bool"...)
|
|
||||||
}
|
|
||||||
if be.GroupModifier.Op != "" {
|
|
||||||
dst = append(dst, ' ')
|
|
||||||
dst = be.GroupModifier.AppendString(dst)
|
|
||||||
}
|
|
||||||
if be.JoinModifier.Op != "" {
|
|
||||||
dst = append(dst, ' ')
|
|
||||||
dst = be.JoinModifier.AppendString(dst)
|
|
||||||
}
|
|
||||||
dst = append(dst, ' ')
|
|
||||||
if _, ok := be.Right.(*BinaryOpExpr); ok {
|
|
||||||
dst = append(dst, '(')
|
|
||||||
dst = be.Right.AppendString(dst)
|
|
||||||
dst = append(dst, ')')
|
|
||||||
} else {
|
|
||||||
dst = be.Right.AppendString(dst)
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// A ModifierExpr represents a modifier attached to a parent expression
|
|
||||||
type ModifierExpr struct {
|
|
||||||
Op string
|
|
||||||
|
|
||||||
Args []string
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (me *ModifierExpr) AppendString(dst []byte) []byte {
|
|
||||||
dst = append(dst, me.Op...)
|
|
||||||
dst = append(dst, " ("...)
|
|
||||||
for i, arg := range me.Args {
|
|
||||||
dst = append(dst, arg...)
|
|
||||||
if i+1 < len(me.Args) {
|
|
||||||
dst = append(dst, ", "...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dst = append(dst, ')')
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
func appendStringArgListExpr(dst []byte, args []Expr) []byte {
|
|
||||||
dst = append(dst, '(')
|
|
||||||
for i, arg := range args {
|
|
||||||
dst = arg.AppendString(dst)
|
|
||||||
if i+1 < len(args) {
|
|
||||||
dst = append(dst, ", "...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dst = append(dst, ')')
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// A FuncExpr represents a function invocation
|
|
||||||
type FuncExpr struct {
|
|
||||||
Name string
|
|
||||||
|
|
||||||
Args []Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (fe *FuncExpr) AppendString(dst []byte) []byte {
|
|
||||||
dst = append(dst, fe.Name...)
|
|
||||||
dst = appendStringArgListExpr(dst, fe.Args)
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// An AggrFuncExpr represents the invocation of an aggregate function
|
|
||||||
type AggrFuncExpr struct {
|
|
||||||
Name string
|
|
||||||
|
|
||||||
Args []Expr
|
|
||||||
|
|
||||||
Modifier ModifierExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (ae *AggrFuncExpr) AppendString(dst []byte) []byte {
|
|
||||||
dst = append(dst, ae.Name...)
|
|
||||||
dst = appendStringArgListExpr(dst, ae.Args)
|
|
||||||
if ae.Modifier.Op != "" {
|
|
||||||
dst = append(dst, ' ')
|
|
||||||
dst = ae.Modifier.AppendString(dst)
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// A WithExpr represents a With expression
|
|
||||||
type WithExpr struct {
|
|
||||||
Was []*WithArgExpr
|
|
||||||
Expr Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (we *WithExpr) AppendString(dst []byte) []byte {
|
|
||||||
dst = append(dst, "WITH ("...)
|
|
||||||
for i, wa := range we.Was {
|
|
||||||
dst = wa.AppendString(dst)
|
|
||||||
if i+1 < len(we.Was) {
|
|
||||||
dst = append(dst, ',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dst = append(dst, ") "...)
|
|
||||||
dst = we.Expr.AppendString(dst)
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// A WithArgExpr represents an arg in a With expression
|
|
||||||
type WithArgExpr struct {
|
|
||||||
Name string
|
|
||||||
Args []string
|
|
||||||
Expr Expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (wa *WithArgExpr) AppendString(dst []byte) []byte {
|
|
||||||
dst = append(dst, wa.Name...)
|
|
||||||
if len(wa.Args) > 0 {
|
|
||||||
dst = append(dst, '(')
|
|
||||||
for i, arg := range wa.Args {
|
|
||||||
dst = append(dst, arg...)
|
|
||||||
if i+1 < len(wa.Args) {
|
|
||||||
dst = append(dst, ',')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dst = append(dst, ')')
|
|
||||||
}
|
|
||||||
dst = append(dst, " = "...)
|
|
||||||
dst = wa.Expr.AppendString(dst)
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// A RollupExpr represents a rollup expression
|
|
||||||
type RollupExpr struct {
|
|
||||||
// The expression for the rollup. Usually it is metricExpr, but may be arbitrary expr
|
|
||||||
// if subquery is used. https://prometheus.io/blog/2019/01/28/subquery-support/
|
|
||||||
Expr Expr
|
|
||||||
|
|
||||||
// Window contains optional window value from square brackets
|
|
||||||
//
|
|
||||||
// For example, `http_requests_total[5m]` will have Window value `5m`.
|
|
||||||
Window string
|
|
||||||
|
|
||||||
// Offset contains optional value from `offset` part.
|
|
||||||
//
|
|
||||||
// For example, `foobar{baz="aa"} offset 5m` will have Offset value `5m`.
|
|
||||||
Offset string
|
|
||||||
|
|
||||||
// Step contains optional step value from square brackets.
|
|
||||||
//
|
|
||||||
// For example, `foobar[1h:3m]` will have Step value '3m'.
|
|
||||||
Step string
|
|
||||||
|
|
||||||
// If set to true, then `foo[1h:]` would print the same
|
|
||||||
// instead of `foo[1h]`.
|
|
||||||
InheritStep bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// ForSubquery returns whether is rollup is for a subquery
|
|
||||||
func (re *RollupExpr) ForSubquery() bool {
|
|
||||||
return len(re.Step) > 0 || re.InheritStep
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (re *RollupExpr) AppendString(dst []byte) []byte {
|
|
||||||
needParens := func() bool {
|
|
||||||
if _, ok := re.Expr.(*RollupExpr); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if _, ok := re.Expr.(*BinaryOpExpr); ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if ae, ok := re.Expr.(*AggrFuncExpr); ok && ae.Modifier.Op != "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}()
|
|
||||||
if needParens {
|
|
||||||
dst = append(dst, '(')
|
|
||||||
}
|
|
||||||
dst = re.Expr.AppendString(dst)
|
|
||||||
if needParens {
|
|
||||||
dst = append(dst, ')')
|
|
||||||
}
|
|
||||||
if len(re.Window) > 0 || re.InheritStep || len(re.Step) > 0 {
|
|
||||||
dst = append(dst, '[')
|
|
||||||
if len(re.Window) > 0 {
|
|
||||||
dst = append(dst, re.Window...)
|
|
||||||
}
|
|
||||||
if len(re.Step) > 0 {
|
|
||||||
dst = append(dst, ':')
|
|
||||||
dst = append(dst, re.Step...)
|
|
||||||
} else if re.InheritStep {
|
|
||||||
dst = append(dst, ':')
|
|
||||||
}
|
|
||||||
dst = append(dst, ']')
|
|
||||||
}
|
|
||||||
if len(re.Offset) > 0 {
|
|
||||||
dst = append(dst, " offset "...)
|
|
||||||
dst = append(dst, re.Offset...)
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// A MetricExpr represents a metric expression
|
|
||||||
type MetricExpr struct {
|
|
||||||
// TagFilters contains a list of tag filters from curly braces.
|
|
||||||
// The first item may be the metric name.
|
|
||||||
TagFilters []TagFilter
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (me *MetricExpr) AppendString(dst []byte) []byte {
|
|
||||||
tfs := me.TagFilters
|
|
||||||
if len(tfs) > 0 {
|
|
||||||
tf := &tfs[0]
|
|
||||||
if len(tf.Key) == 0 && !tf.IsNegative && !tf.IsRegexp {
|
|
||||||
dst = appendEscapedIdent(dst, tf.Value)
|
|
||||||
tfs = tfs[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(tfs) > 0 {
|
|
||||||
dst = append(dst, '{')
|
|
||||||
for i := range tfs {
|
|
||||||
tf := &tfs[i]
|
|
||||||
dst = appendStringTagFilter(dst, tf)
|
|
||||||
if i+1 < len(tfs) {
|
|
||||||
dst = append(dst, ", "...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dst = append(dst, '}')
|
|
||||||
} else if len(me.TagFilters) == 0 {
|
|
||||||
dst = append(dst, "{}"...)
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsEmpty returns whether this is an empty metric expression
|
|
||||||
func (me *MetricExpr) IsEmpty() bool {
|
|
||||||
return len(me.TagFilters) == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsOnlyMetricGroup returns whether this is a metric group only
|
|
||||||
func (me *MetricExpr) IsOnlyMetricGroup() bool {
|
|
||||||
if !me.HasNonEmptyMetricGroup() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return len(me.TagFilters) == 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasNonEmptyMetricGroup returns whether this has a non-empty metric group
|
|
||||||
func (me *MetricExpr) HasNonEmptyMetricGroup() bool {
|
|
||||||
if len(me.TagFilters) == 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
tf := &me.TagFilters[0]
|
|
||||||
return len(tf.Key) == 0 && !tf.IsNegative && !tf.IsRegexp
|
|
||||||
}
|
|
||||||
|
|
||||||
// A TagFilter is a single key <op> value filter tag in a metric filter
|
|
||||||
//
|
|
||||||
// Note that this should exactly match the definition in the stroage package
|
|
||||||
type TagFilter struct {
|
|
||||||
Key []byte
|
|
||||||
Value []byte
|
|
||||||
IsNegative bool
|
|
||||||
IsRegexp bool
|
|
||||||
}
|
|
||||||
|
|
||||||
// A MetricTemplateExpr represents a metric expression prior to expansion via
|
|
||||||
// a with clause
|
|
||||||
type MetricTemplateExpr struct {
|
|
||||||
TagFilters []*TagFilterExpr
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (mte *MetricTemplateExpr) AppendString(dst []byte) []byte {
|
|
||||||
tfs := mte.TagFilters
|
|
||||||
if len(tfs) > 0 {
|
|
||||||
tf := tfs[0]
|
|
||||||
if len(tf.Key) == 0 && !tf.IsNegative && !tf.IsRegexp && len(tf.Value.Tokens) == 1 && !tf.Value.Tokens[0].Ident {
|
|
||||||
dst = appendEscapedIdent(dst, []byte(tf.Value.Tokens[0].S))
|
|
||||||
tfs = tfs[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(tfs) > 0 {
|
|
||||||
dst = append(dst, '{')
|
|
||||||
for i := range tfs {
|
|
||||||
tf := tfs[i]
|
|
||||||
dst = tf.AppendString(dst)
|
|
||||||
if i+1 < len(tfs) {
|
|
||||||
dst = append(dst, ", "...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dst = append(dst, '}')
|
|
||||||
} else if len(mte.TagFilters) == 0 {
|
|
||||||
dst = append(dst, "{}"...)
|
|
||||||
}
|
|
||||||
return dst
|
|
||||||
}
|
|
||||||
|
|
||||||
// A TagFilterExpr represents a tag filter
|
|
||||||
type TagFilterExpr struct {
|
|
||||||
Key string
|
|
||||||
Value *StringTemplateExpr
|
|
||||||
IsRegexp bool
|
|
||||||
IsNegative bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (tfe *TagFilterExpr) String() string {
|
|
||||||
return fmt.Sprintf("[key=%q, value=%+v, isRegexp=%v, isNegative=%v]", tfe.Key, tfe.Value, tfe.IsRegexp, tfe.IsNegative)
|
|
||||||
}
|
|
||||||
|
|
||||||
// AppendString appends string representation of expr to dst.
|
|
||||||
func (tfe *TagFilterExpr) AppendString(dst []byte) []byte {
|
|
||||||
if len(tfe.Key) == 0 {
|
|
||||||
dst = append(dst, "__name__"...)
|
|
||||||
} else {
|
|
||||||
dst = append(dst, tfe.Key...)
|
|
||||||
}
|
|
||||||
dst = appendStringTagFilterOp(dst, tfe.IsRegexp, tfe.IsNegative)
|
|
||||||
return tfe.Value.AppendString(dst)
|
|
||||||
}
|
|
1298
lib/promql/parser.go
1298
lib/promql/parser.go
File diff suppressed because it is too large
Load diff
|
@ -1,47 +0,0 @@
|
||||||
package promql
|
|
||||||
|
|
||||||
// A Visitor is used to walk a parsed query
|
|
||||||
type Visitor interface {
|
|
||||||
Visit(expr Expr) Visitor
|
|
||||||
}
|
|
||||||
|
|
||||||
// Walk invokes Visit on v for each node in the parsed query tree
|
|
||||||
func Walk(expr Expr, v Visitor) {
|
|
||||||
nv := v.Visit(expr)
|
|
||||||
if nv == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch t := expr.(type) {
|
|
||||||
case *ParensExpr:
|
|
||||||
for _, e := range *t {
|
|
||||||
Walk(e, nv)
|
|
||||||
}
|
|
||||||
case *BinaryOpExpr:
|
|
||||||
Walk(t.Left, nv)
|
|
||||||
Walk(t.Right, nv)
|
|
||||||
case *FuncExpr:
|
|
||||||
for _, ae := range t.Args {
|
|
||||||
Walk(ae, nv)
|
|
||||||
}
|
|
||||||
case *AggrFuncExpr:
|
|
||||||
for _, ae := range t.Args {
|
|
||||||
Walk(ae, nv)
|
|
||||||
}
|
|
||||||
Walk(&t.Modifier, nv)
|
|
||||||
case *WithExpr:
|
|
||||||
for _, wa := range t.Was {
|
|
||||||
Walk(wa, nv)
|
|
||||||
}
|
|
||||||
Walk(t.Expr, nv)
|
|
||||||
case *WithArgExpr:
|
|
||||||
Walk(t.Expr, nv)
|
|
||||||
case *RollupExpr:
|
|
||||||
Walk(t.Expr, nv)
|
|
||||||
case *MetricTemplateExpr:
|
|
||||||
for _, tfe := range t.TagFilters {
|
|
||||||
Walk(tfe, nv)
|
|
||||||
}
|
|
||||||
case *TagFilterExpr:
|
|
||||||
Walk(t.Value, nv)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,69 +0,0 @@
|
||||||
package promql
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestWalk(t *testing.T) {
|
|
||||||
e, err := testParser.ParseRawPromQL(`
|
|
||||||
WITH (
|
|
||||||
rf(a, b) = a + b
|
|
||||||
)
|
|
||||||
rf(metric1{foo="bar"}, metric2) or (sum(abs(changes(metric3))))
|
|
||||||
`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error when parsing: %s", err)
|
|
||||||
}
|
|
||||||
var cv collectVisitor
|
|
||||||
Walk(e, &cv)
|
|
||||||
expected := []string{
|
|
||||||
`*promql.WithExpr WITH (rf(a,b) = a + b) rf(metric1{foo="bar"}, metric2) or (sum(abs(changes(metric3))))`,
|
|
||||||
`*promql.WithArgExpr rf(a,b) = a + b`,
|
|
||||||
`*promql.BinaryOpExpr a + b`,
|
|
||||||
`*promql.MetricTemplateExpr a`,
|
|
||||||
`*promql.TagFilterExpr __name__="a"`,
|
|
||||||
`*promql.StringTemplateExpr "a"`,
|
|
||||||
`*promql.MetricTemplateExpr b`,
|
|
||||||
`*promql.TagFilterExpr __name__="b"`,
|
|
||||||
`*promql.StringTemplateExpr "b"`,
|
|
||||||
`*promql.BinaryOpExpr rf(metric1{foo="bar"}, metric2) or (sum(abs(changes(metric3))))`,
|
|
||||||
`*promql.FuncExpr rf(metric1{foo="bar"}, metric2)`,
|
|
||||||
`*promql.MetricTemplateExpr metric1{foo="bar"}`,
|
|
||||||
`*promql.TagFilterExpr __name__="metric1"`,
|
|
||||||
`*promql.StringTemplateExpr "metric1"`,
|
|
||||||
`*promql.TagFilterExpr foo="bar"`,
|
|
||||||
`*promql.StringTemplateExpr "bar"`,
|
|
||||||
`*promql.MetricTemplateExpr metric2`,
|
|
||||||
`*promql.TagFilterExpr __name__="metric2"`,
|
|
||||||
`*promql.StringTemplateExpr "metric2"`,
|
|
||||||
`*promql.ParensExpr (sum(abs(changes(metric3))))`,
|
|
||||||
`*promql.AggrFuncExpr sum(abs(changes(metric3)))`,
|
|
||||||
`*promql.FuncExpr abs(changes(metric3))`,
|
|
||||||
`*promql.FuncExpr changes(metric3)`,
|
|
||||||
`*promql.MetricTemplateExpr metric3`,
|
|
||||||
`*promql.TagFilterExpr __name__="metric3"`,
|
|
||||||
`*promql.StringTemplateExpr "metric3"`,
|
|
||||||
`*promql.ModifierExpr ()`,
|
|
||||||
}
|
|
||||||
if len(cv.visited) != len(expected) {
|
|
||||||
t.Fatal("Expected", len(expected), "elements visited, got", len(cv.visited))
|
|
||||||
}
|
|
||||||
for i, v := range cv.visited {
|
|
||||||
if strings.TrimSpace(v) != expected[i] {
|
|
||||||
t.Fatalf("Expected %s, got %s at position %v", expected[i], v, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type collectVisitor struct {
|
|
||||||
visited []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cv *collectVisitor) Visit(e Expr) Visitor {
|
|
||||||
sb := e.AppendString(nil)
|
|
||||||
s := fmt.Sprintf("%T %v\n", e, string(sb))
|
|
||||||
cv.visited = append(cv.visited, s)
|
|
||||||
return cv
|
|
||||||
}
|
|
Loading…
Reference in a new issue