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/promql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
"github.com/valyala/quicktemplate"
|
||||
|
@ -634,14 +635,14 @@ func parseDuration(s string, step int64) (int64, error) {
|
|||
if len(s) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return promql.DurationValue(s, step)
|
||||
return metricsql.DurationValue(s, step)
|
||||
}
|
||||
|
||||
func parsePositiveDuration(s string, step int64) (int64, error) {
|
||||
if len(s) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return promql.PositiveDurationValue(s, step)
|
||||
return metricsql.PositiveDurationValue(s, step)
|
||||
}
|
||||
|
||||
// QueryRangeHandler processes /api/v1/query_range request.
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"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/metrics"
|
||||
"github.com/valyala/histogram"
|
||||
|
@ -49,7 +49,7 @@ type aggrFunc func(afa *aggrFuncArg) ([]*timeseries, error)
|
|||
|
||||
type aggrFuncArg struct {
|
||||
args [][]*timeseries
|
||||
ae *promql.AggrFuncExpr
|
||||
ae *metricsql.AggrFuncExpr
|
||||
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)
|
||||
switch groupOp {
|
||||
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)
|
||||
|
||||
// Perform grouping.
|
||||
|
|
|
@ -5,11 +5,11 @@ import (
|
|||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
)
|
||||
|
||||
// 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.
|
||||
var incrementalAggrFuncCallbacksMap = map[string]*incrementalAggrFuncCallbacks{
|
||||
|
@ -51,7 +51,7 @@ var incrementalAggrFuncCallbacksMap = map[string]*incrementalAggrFuncCallbacks{
|
|||
}
|
||||
|
||||
type incrementalAggrFuncContext struct {
|
||||
ae *promql.AggrFuncExpr
|
||||
ae *metricsql.AggrFuncExpr
|
||||
|
||||
mLock sync.Mutex
|
||||
m map[uint]map[string]*incrementalAggrContext
|
||||
|
@ -59,7 +59,7 @@ type incrementalAggrFuncContext struct {
|
|||
callbacks *incrementalAggrFuncCallbacks
|
||||
}
|
||||
|
||||
func newIncrementalAggrFuncContext(ae *promql.AggrFuncExpr, callbacks *incrementalAggrFuncCallbacks) *incrementalAggrFuncContext {
|
||||
func newIncrementalAggrFuncContext(ae *metricsql.AggrFuncExpr, callbacks *incrementalAggrFuncCallbacks) *incrementalAggrFuncContext {
|
||||
return &incrementalAggrFuncContext{
|
||||
ae: ae,
|
||||
m: make(map[uint]map[string]*incrementalAggrContext),
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
)
|
||||
|
||||
func TestIncrementalAggr(t *testing.T) {
|
||||
|
@ -44,7 +44,7 @@ func TestIncrementalAggr(t *testing.T) {
|
|||
f := func(name string, valuesExpected []float64) {
|
||||
t.Helper()
|
||||
callbacks := getIncrementalAggrFuncCallbacks(name)
|
||||
ae := &promql.AggrFuncExpr{
|
||||
ae := &metricsql.AggrFuncExpr{
|
||||
Name: name,
|
||||
}
|
||||
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"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
var binaryOpFuncs = map[string]binaryOpFunc{
|
||||
"+": newBinaryOpArithFunc(binaryOpPlus),
|
||||
"-": newBinaryOpArithFunc(binaryOpMinus),
|
||||
"*": newBinaryOpArithFunc(binaryOpMul),
|
||||
"/": newBinaryOpArithFunc(binaryOpDiv),
|
||||
"%": newBinaryOpArithFunc(binaryOpMod),
|
||||
"^": newBinaryOpArithFunc(binaryOpPow),
|
||||
"+": newBinaryOpArithFunc(binaryop.Plus),
|
||||
"-": newBinaryOpArithFunc(binaryop.Minus),
|
||||
"*": newBinaryOpArithFunc(binaryop.Mul),
|
||||
"/": newBinaryOpArithFunc(binaryop.Div),
|
||||
"%": newBinaryOpArithFunc(binaryop.Mod),
|
||||
"^": newBinaryOpArithFunc(binaryop.Pow),
|
||||
|
||||
// cmp ops
|
||||
"==": newBinaryOpCmpFunc(binaryOpEq),
|
||||
"!=": newBinaryOpCmpFunc(binaryOpNeq),
|
||||
">": newBinaryOpCmpFunc(binaryOpGt),
|
||||
"<": newBinaryOpCmpFunc(binaryOpLt),
|
||||
">=": newBinaryOpCmpFunc(binaryOpGte),
|
||||
"<=": newBinaryOpCmpFunc(binaryOpLte),
|
||||
"==": newBinaryOpCmpFunc(binaryop.Eq),
|
||||
"!=": newBinaryOpCmpFunc(binaryop.Neq),
|
||||
">": newBinaryOpCmpFunc(binaryop.Gt),
|
||||
"<": newBinaryOpCmpFunc(binaryop.Lt),
|
||||
">=": newBinaryOpCmpFunc(binaryop.Gte),
|
||||
"<=": newBinaryOpCmpFunc(binaryop.Lte),
|
||||
|
||||
// logical set ops
|
||||
"and": binaryOpAnd,
|
||||
|
@ -32,9 +33,9 @@ var binaryOpFuncs = map[string]binaryOpFunc{
|
|||
"unless": binaryOpUnless,
|
||||
|
||||
// New op
|
||||
"if": newBinaryOpArithFunc(binaryOpIf),
|
||||
"ifnot": newBinaryOpArithFunc(binaryOpIfnot),
|
||||
"default": newBinaryOpArithFunc(binaryOpDefault),
|
||||
"if": newBinaryOpArithFunc(binaryop.If),
|
||||
"ifnot": newBinaryOpArithFunc(binaryop.Ifnot),
|
||||
"default": newBinaryOpArithFunc(binaryop.Default),
|
||||
}
|
||||
|
||||
func getBinaryOpFunc(op string) binaryOpFunc {
|
||||
|
@ -42,80 +43,8 @@ func getBinaryOpFunc(op string) binaryOpFunc {
|
|||
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 {
|
||||
be *promql.BinaryOpExpr
|
||||
be *metricsql.BinaryOpExpr
|
||||
left []*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 isScalar(left) {
|
||||
// Fast path: `scalar op vector`
|
||||
|
@ -256,7 +185,7 @@ func adjustBinaryOpTags(be *promql.BinaryOpExpr, left, right []*timeseries) ([]*
|
|||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
var m map[string]*timeseries
|
||||
for _, tsLeft := range tssLeft {
|
||||
|
@ -340,8 +269,8 @@ func mergeNonOverlappingTimeseries(dst, src *timeseries) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func resetMetricGroupIfRequired(be *promql.BinaryOpExpr, ts *timeseries) {
|
||||
if isBinaryOpCmp(be.Op) && !be.Bool {
|
||||
func resetMetricGroupIfRequired(be *metricsql.BinaryOpExpr, ts *timeseries) {
|
||||
if metricsql.IsBinaryOpCmp(be.Op) && !be.Bool {
|
||||
// Do not reset MetricGroup for non-boolean `compare` binary ops like Prometheus does.
|
||||
return
|
||||
}
|
||||
|
@ -353,90 +282,6 @@ func resetMetricGroupIfRequired(be *promql.BinaryOpExpr, ts *timeseries) {
|
|||
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) {
|
||||
mLeft, mRight := createTimeseriesMapByTagSet(bfa.be, bfa.left, bfa.right)
|
||||
var rvs []*timeseries
|
||||
|
@ -473,7 +318,7 @@ func binaryOpUnless(bfa *binaryOpFuncArg) ([]*timeseries, error) {
|
|||
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
|
||||
groupOp := strings.ToLower(be.GroupModifier.Op)
|
||||
if len(groupOp) == 0 {
|
||||
|
|
|
@ -6,13 +6,12 @@ import (
|
|||
"math"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/bytesutil"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
|
||||
"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/metrics"
|
||||
)
|
||||
|
@ -154,9 +153,9 @@ func getTimestamps(start, end, step int64) []int64 {
|
|||
return timestamps
|
||||
}
|
||||
|
||||
func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
||||
if me, ok := e.(*promql.MetricExpr); ok {
|
||||
re := &promql.RollupExpr{
|
||||
func evalExpr(ec *EvalConfig, e metricsql.Expr) ([]*timeseries, error) {
|
||||
if me, ok := e.(*metricsql.MetricExpr); ok {
|
||||
re := &metricsql.RollupExpr{
|
||||
Expr: me,
|
||||
}
|
||||
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
|
||||
}
|
||||
if re, ok := e.(*promql.RollupExpr); ok {
|
||||
if re, ok := e.(*metricsql.RollupExpr); ok {
|
||||
rv, err := evalRollupFunc(ec, "default_rollup", rollupDefault, re, nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`cannot evaluate %q: %s`, re.AppendString(nil), err)
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
if fe, ok := e.(*promql.FuncExpr); ok {
|
||||
if fe, ok := e.(*metricsql.FuncExpr); ok {
|
||||
nrf := getRollupFunc(fe.Name)
|
||||
if nrf == nil {
|
||||
args, err := evalExprs(ec, fe.Args)
|
||||
|
@ -208,11 +207,11 @@ func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
|||
}
|
||||
return rv, nil
|
||||
}
|
||||
if ae, ok := e.(*promql.AggrFuncExpr); ok {
|
||||
if ae, ok := e.(*metricsql.AggrFuncExpr); ok {
|
||||
if callbacks := getIncrementalAggrFuncCallbacks(ae.Name); callbacks != nil {
|
||||
fe, nrf := tryGetArgRollupFuncWithMetricExpr(ae)
|
||||
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.
|
||||
args, re, err := evalRollupFuncArgs(ec, fe)
|
||||
if err != nil {
|
||||
|
@ -245,7 +244,7 @@ func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
|||
}
|
||||
return rv, nil
|
||||
}
|
||||
if be, ok := e.(*promql.BinaryOpExpr); ok {
|
||||
if be, ok := e.(*metricsql.BinaryOpExpr); ok {
|
||||
left, err := evalExpr(ec, be.Left)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -269,18 +268,18 @@ func evalExpr(ec *EvalConfig, e promql.Expr) ([]*timeseries, error) {
|
|||
}
|
||||
return rv, nil
|
||||
}
|
||||
if ne, ok := e.(*promql.NumberExpr); ok {
|
||||
if ne, ok := e.(*metricsql.NumberExpr); ok {
|
||||
rv := evalNumber(ec, ne.N)
|
||||
return rv, nil
|
||||
}
|
||||
if se, ok := e.(*promql.StringExpr); ok {
|
||||
if se, ok := e.(*metricsql.StringExpr); ok {
|
||||
rv := evalString(ec, se.S)
|
||||
return rv, 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 {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -291,31 +290,31 @@ func tryGetArgRollupFuncWithMetricExpr(ae *promql.AggrFuncExpr) (*promql.FuncExp
|
|||
// - rollupFunc(metricExpr)
|
||||
// - rollupFunc(metricExpr[d])
|
||||
|
||||
if me, ok := e.(*promql.MetricExpr); ok {
|
||||
if me, ok := e.(*metricsql.MetricExpr); ok {
|
||||
// e = metricExpr
|
||||
if me.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
fe := &promql.FuncExpr{
|
||||
fe := &metricsql.FuncExpr{
|
||||
Name: "default_rollup",
|
||||
Args: []promql.Expr{me},
|
||||
Args: []metricsql.Expr{me},
|
||||
}
|
||||
nrf := getRollupFunc(fe.Name)
|
||||
return fe, nrf
|
||||
}
|
||||
if re, ok := e.(*promql.RollupExpr); ok {
|
||||
if me, ok := re.Expr.(*promql.MetricExpr); !ok || me.IsEmpty() || re.ForSubquery() {
|
||||
if re, ok := e.(*metricsql.RollupExpr); ok {
|
||||
if me, ok := re.Expr.(*metricsql.MetricExpr); !ok || me.IsEmpty() || re.ForSubquery() {
|
||||
return nil, nil
|
||||
}
|
||||
// e = metricExpr[d]
|
||||
fe := &promql.FuncExpr{
|
||||
fe := &metricsql.FuncExpr{
|
||||
Name: "default_rollup",
|
||||
Args: []promql.Expr{re},
|
||||
Args: []metricsql.Expr{re},
|
||||
}
|
||||
nrf := getRollupFunc(fe.Name)
|
||||
return fe, nrf
|
||||
}
|
||||
fe, ok := e.(*promql.FuncExpr)
|
||||
fe, ok := e.(*metricsql.FuncExpr)
|
||||
if !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -325,18 +324,18 @@ func tryGetArgRollupFuncWithMetricExpr(ae *promql.AggrFuncExpr) (*promql.FuncExp
|
|||
}
|
||||
rollupArgIdx := getRollupArgIdx(fe.Name)
|
||||
arg := fe.Args[rollupArgIdx]
|
||||
if me, ok := arg.(*promql.MetricExpr); ok {
|
||||
if me, ok := arg.(*metricsql.MetricExpr); ok {
|
||||
if me.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
// e = rollupFunc(metricExpr)
|
||||
return &promql.FuncExpr{
|
||||
return &metricsql.FuncExpr{
|
||||
Name: fe.Name,
|
||||
Args: []promql.Expr{me},
|
||||
Args: []metricsql.Expr{me},
|
||||
}, nrf
|
||||
}
|
||||
if re, ok := arg.(*promql.RollupExpr); ok {
|
||||
if me, ok := re.Expr.(*promql.MetricExpr); !ok || me.IsEmpty() || re.ForSubquery() {
|
||||
if re, ok := arg.(*metricsql.RollupExpr); ok {
|
||||
if me, ok := re.Expr.(*metricsql.MetricExpr); !ok || me.IsEmpty() || re.ForSubquery() {
|
||||
return nil, nil
|
||||
}
|
||||
// e = rollupFunc(metricExpr[d])
|
||||
|
@ -345,7 +344,7 @@ func tryGetArgRollupFuncWithMetricExpr(ae *promql.AggrFuncExpr) (*promql.FuncExp
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func evalExprs(ec *EvalConfig, es []promql.Expr) ([][]*timeseries, error) {
|
||||
func evalExprs(ec *EvalConfig, es []metricsql.Expr) ([][]*timeseries, error) {
|
||||
var rvs [][]*timeseries
|
||||
for _, e := range es {
|
||||
rv, err := evalExpr(ec, e)
|
||||
|
@ -357,8 +356,8 @@ func evalExprs(ec *EvalConfig, es []promql.Expr) ([][]*timeseries, error) {
|
|||
return rvs, nil
|
||||
}
|
||||
|
||||
func evalRollupFuncArgs(ec *EvalConfig, fe *promql.FuncExpr) ([]interface{}, *promql.RollupExpr, error) {
|
||||
var re *promql.RollupExpr
|
||||
func evalRollupFuncArgs(ec *EvalConfig, fe *metricsql.FuncExpr) ([]interface{}, *metricsql.RollupExpr, error) {
|
||||
var re *metricsql.RollupExpr
|
||||
rollupArgIdx := getRollupArgIdx(fe.Name)
|
||||
args := make([]interface{}, len(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
|
||||
}
|
||||
|
||||
func getRollupExprArg(arg promql.Expr) *promql.RollupExpr {
|
||||
re, ok := arg.(*promql.RollupExpr)
|
||||
func getRollupExprArg(arg metricsql.Expr) *metricsql.RollupExpr {
|
||||
re, ok := arg.(*metricsql.RollupExpr)
|
||||
if !ok {
|
||||
// Wrap non-rollup arg into rollupExpr.
|
||||
return &promql.RollupExpr{
|
||||
// Wrap non-rollup arg into metricsql.RollupExpr.
|
||||
return &metricsql.RollupExpr{
|
||||
Expr: arg,
|
||||
}
|
||||
}
|
||||
|
@ -388,28 +387,28 @@ func getRollupExprArg(arg promql.Expr) *promql.RollupExpr {
|
|||
// Return standard rollup if it doesn't contain subquery.
|
||||
return re
|
||||
}
|
||||
me, ok := re.Expr.(*promql.MetricExpr)
|
||||
me, ok := re.Expr.(*metricsql.MetricExpr)
|
||||
if !ok {
|
||||
// arg contains subquery.
|
||||
return re
|
||||
}
|
||||
// Convert me[w:step] -> default_rollup(me)[w:step]
|
||||
reNew := *re
|
||||
reNew.Expr = &promql.FuncExpr{
|
||||
reNew.Expr = &metricsql.FuncExpr{
|
||||
Name: "default_rollup",
|
||||
Args: []promql.Expr{
|
||||
&promql.RollupExpr{Expr: me},
|
||||
Args: []metricsql.Expr{
|
||||
&metricsql.RollupExpr{Expr: me},
|
||||
},
|
||||
}
|
||||
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
|
||||
var offset int64
|
||||
if len(re.Offset) > 0 {
|
||||
var err error
|
||||
offset, err = promql.DurationValue(re.Offset, ec.Step)
|
||||
offset, err = metricsql.DurationValue(re.Offset, ec.Step)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -425,7 +424,7 @@ func evalRollupFunc(ec *EvalConfig, name string, rf rollupFunc, re *promql.Rollu
|
|||
}
|
||||
var rvs []*timeseries
|
||||
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)
|
||||
} else {
|
||||
if iafc != nil {
|
||||
|
@ -450,12 +449,12 @@ func evalRollupFunc(ec *EvalConfig, name string, rf rollupFunc, re *promql.Rollu
|
|||
return rvs, nil
|
||||
}
|
||||
|
||||
func evalRollupFuncWithSubquery(ec *EvalConfig, name string, rf rollupFunc, re *promql.RollupExpr) ([]*timeseries, error) {
|
||||
// Do not use rollupResultCacheV here, since it works only with metricExpr.
|
||||
func evalRollupFuncWithSubquery(ec *EvalConfig, name string, rf rollupFunc, re *metricsql.RollupExpr) ([]*timeseries, error) {
|
||||
// Do not use rollupResultCacheV here, since it works only with metricsql.MetricExpr.
|
||||
var step int64
|
||||
if len(re.Step) > 0 {
|
||||
var err error
|
||||
step, err = promql.DurationValue(re.Step, ec.Step)
|
||||
step, err = metricsql.PositiveDurationValue(re.Step, ec.Step)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -465,7 +464,7 @@ func evalRollupFuncWithSubquery(ec *EvalConfig, name string, rf rollupFunc, re *
|
|||
var window int64
|
||||
if len(re.Window) > 0 {
|
||||
var err error
|
||||
window, err = promql.DurationValue(re.Window, ec.Step)
|
||||
window, err = metricsql.PositiveDurationValue(re.Window, ec.Step)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -559,14 +558,14 @@ var (
|
|||
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() {
|
||||
return evalNumber(ec, nan), nil
|
||||
}
|
||||
var window int64
|
||||
if len(windowStr) > 0 {
|
||||
var err error
|
||||
window, err = promql.DurationValue(windowStr, ec.Step)
|
||||
window, err = metricsql.PositiveDurationValue(windowStr, ec.Step)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -586,12 +585,11 @@ func evalRollupFuncWithMetricExpr(ec *EvalConfig, name string, rf rollupFunc, me
|
|||
}
|
||||
|
||||
// Fetch the remaining part of the result.
|
||||
tfs := toTagFilters(me.LabelFilters)
|
||||
sq := &storage.SearchQuery{
|
||||
MinTimestamp: start - window - maxSilenceInterval,
|
||||
MaxTimestamp: ec.End + ec.Step,
|
||||
TagFilterss: [][]storage.TagFilter{
|
||||
*(*[]storage.TagFilter)(unsafe.Pointer(&me.TagFilters)),
|
||||
},
|
||||
TagFilterss: [][]storage.TagFilter{tfs},
|
||||
}
|
||||
rss, err := netstorage.ProcessSearchQuery(sq, true, ec.Deadline)
|
||||
if err != nil {
|
||||
|
@ -815,3 +813,23 @@ func mulNoOverflow(a, b int64) int64 {
|
|||
}
|
||||
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/lib/logger"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
)
|
||||
|
||||
|
@ -86,12 +86,12 @@ func Exec(ec *EvalConfig, q string, isFirstPointOnly bool) ([]netstorage.Result,
|
|||
return result, err
|
||||
}
|
||||
|
||||
func maySortResults(e promql.Expr, tss []*timeseries) bool {
|
||||
func maySortResults(e metricsql.Expr, tss []*timeseries) bool {
|
||||
if len(tss) > 100 {
|
||||
// There is no sense in sorting a lot of results
|
||||
return false
|
||||
}
|
||||
fe, ok := e.(*promql.FuncExpr)
|
||||
fe, ok := e.(*metricsql.FuncExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
@ -155,19 +155,10 @@ func removeNaNs(tss []*timeseries) []*timeseries {
|
|||
return rvs
|
||||
}
|
||||
|
||||
func parsePromQL(q string) (promql.Expr, error) {
|
||||
e, err := parser.ParsePromQL(q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return simplifyConstants(e), nil
|
||||
}
|
||||
|
||||
func parsePromQLWithCache(q string) (promql.Expr, error) {
|
||||
func parsePromQLWithCache(q string) (metricsql.Expr, error) {
|
||||
pcv := parseCacheV.Get(q)
|
||||
if pcv == nil {
|
||||
e, err := parsePromQL(q)
|
||||
e, err := metricsql.Parse(q)
|
||||
pcv = &parseCacheValue{
|
||||
e: e,
|
||||
err: err,
|
||||
|
@ -180,8 +171,6 @@ func parsePromQLWithCache(q string) (promql.Expr, error) {
|
|||
return pcv.e, nil
|
||||
}
|
||||
|
||||
var parser = promql.NewParser(compileRegexpAnchored)
|
||||
|
||||
var parseCacheV = func() *parseCache {
|
||||
pc := &parseCache{
|
||||
m: make(map[string]*parseCacheValue),
|
||||
|
@ -201,7 +190,7 @@ var parseCacheV = func() *parseCache {
|
|||
const parseCacheMaxLen = 10e3
|
||||
|
||||
type parseCacheValue struct {
|
||||
e promql.Expr
|
||||
e metricsql.Expr
|
||||
err error
|
||||
}
|
||||
|
||||
|
@ -261,7 +250,3 @@ func (pc *parseCache) Put(q string, pcv *parseCacheValue) {
|
|||
pc.m[q] = pcv
|
||||
pc.mu.Unlock()
|
||||
}
|
||||
|
||||
func init() {
|
||||
promql.Panicf = logger.Panicf
|
||||
}
|
||||
|
|
|
@ -2,12 +2,27 @@ package promql
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
"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
|
||||
// wrapped into rollup.
|
||||
//
|
||||
|
@ -17,12 +32,12 @@ func IsMetricSelectorWithRollup(s string) (childQuery string, window, offset str
|
|||
if err != nil {
|
||||
return
|
||||
}
|
||||
re, ok := expr.(*promql.RollupExpr)
|
||||
re, ok := expr.(*metricsql.RollupExpr)
|
||||
if !ok || len(re.Window) == 0 || len(re.Step) > 0 {
|
||||
return
|
||||
}
|
||||
me, ok := re.Expr.(*promql.MetricExpr)
|
||||
if !ok || len(me.TagFilters) == 0 {
|
||||
me, ok := re.Expr.(*metricsql.MetricExpr)
|
||||
if !ok || len(me.LabelFilters) == 0 {
|
||||
return
|
||||
}
|
||||
wrappedQuery := me.AppendString(nil)
|
||||
|
@ -30,18 +45,19 @@ func IsMetricSelectorWithRollup(s string) (childQuery string, window, offset str
|
|||
}
|
||||
|
||||
// ParseMetricSelector parses s containing PromQL metric selector
|
||||
// and returns the corresponding TagFilters.
|
||||
// and returns the corresponding LabelFilters.
|
||||
func ParseMetricSelector(s string) ([]storage.TagFilter, error) {
|
||||
expr, err := parsePromQLWithCache(s)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
me, ok := expr.(*promql.MetricExpr)
|
||||
me, ok := expr.(*metricsql.MetricExpr)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expecting metricSelector; got %q", expr.AppendString(nil))
|
||||
}
|
||||
if len(me.TagFilters) == 0 {
|
||||
return nil, fmt.Errorf("tagFilters cannot be empty")
|
||||
if len(me.LabelFilters) == 0 {
|
||||
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/logger"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/memory"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/workingsetcache"
|
||||
"github.com/VictoriaMetrics/fastcache"
|
||||
"github.com/VictoriaMetrics/metrics"
|
||||
|
@ -130,7 +129,7 @@ func ResetRollupResultCache() {
|
|||
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() {
|
||||
return nil, ec.Start
|
||||
}
|
||||
|
@ -211,7 +210,7 @@ func (rrc *rollupResultCache) Get(funcName string, ec *EvalConfig, me *promql.Me
|
|||
|
||||
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() {
|
||||
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.
|
||||
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)
|
||||
if iafc == nil {
|
||||
dst = append(dst, 0)
|
||||
|
@ -304,9 +303,9 @@ func marshalRollupResultCacheKey(dst []byte, funcName string, me *promql.MetricE
|
|||
dst = append(dst, funcName...)
|
||||
dst = encoding.MarshalInt64(dst, window)
|
||||
dst = encoding.MarshalInt64(dst, step)
|
||||
for i := range me.TagFilters {
|
||||
stf := storage.TagFilter(me.TagFilters[i])
|
||||
dst = stf.Marshal(dst)
|
||||
tfs := toTagFilters(me.LabelFilters)
|
||||
for i := range tfs {
|
||||
dst = tfs[i].Marshal(dst)
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package promql
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||
)
|
||||
|
||||
|
@ -18,14 +18,14 @@ func TestRollupResultCache(t *testing.T) {
|
|||
|
||||
MayCache: true,
|
||||
}
|
||||
me := &promql.MetricExpr{
|
||||
TagFilters: []promql.TagFilter{{
|
||||
Key: []byte("aaa"),
|
||||
Value: []byte("xxx"),
|
||||
me := &metricsql.MetricExpr{
|
||||
LabelFilters: []metricsql.LabelFilter{{
|
||||
Label: "aaa",
|
||||
Value: "xxx",
|
||||
}},
|
||||
}
|
||||
iafc := &incrementalAggrFuncContext{
|
||||
ae: &promql.AggrFuncExpr{
|
||||
ae: &metricsql.AggrFuncExpr{
|
||||
Name: "foobar",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package promql
|
||||
|
||||
import (
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -158,7 +159,7 @@ func TestDerivValues(t *testing.T) {
|
|||
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()
|
||||
nrf := getRollupFunc(funcName)
|
||||
if nrf == nil {
|
||||
|
@ -198,8 +199,8 @@ func TestRollupQuantileOverTime(t *testing.T) {
|
|||
Values: []float64{phi},
|
||||
Timestamps: []int64{123},
|
||||
}}
|
||||
var me promql.MetricExpr
|
||||
args := []interface{}{phis, &promql.RollupExpr{Expr: &me}}
|
||||
var me metricsql.MetricExpr
|
||||
args := []interface{}{phis, &metricsql.RollupExpr{Expr: &me}}
|
||||
testRollupFunc(t, "quantile_over_time", args, &me, vExpected)
|
||||
}
|
||||
|
||||
|
@ -220,8 +221,8 @@ func TestRollupPredictLinear(t *testing.T) {
|
|||
Values: []float64{sec},
|
||||
Timestamps: []int64{123},
|
||||
}}
|
||||
var me promql.MetricExpr
|
||||
args := []interface{}{&promql.RollupExpr{Expr: &me}, secs}
|
||||
var me metricsql.MetricExpr
|
||||
args := []interface{}{&metricsql.RollupExpr{Expr: &me}, secs}
|
||||
testRollupFunc(t, "predict_linear", args, &me, vExpected)
|
||||
}
|
||||
|
||||
|
@ -242,8 +243,8 @@ func TestRollupHoltWinters(t *testing.T) {
|
|||
Values: []float64{tf},
|
||||
Timestamps: []int64{123},
|
||||
}}
|
||||
var me promql.MetricExpr
|
||||
args := []interface{}{&promql.RollupExpr{Expr: &me}, sfs, tfs}
|
||||
var me metricsql.MetricExpr
|
||||
args := []interface{}{&metricsql.RollupExpr{Expr: &me}, sfs, tfs}
|
||||
testRollupFunc(t, "holt_winters", args, &me, vExpected)
|
||||
}
|
||||
|
||||
|
@ -266,8 +267,8 @@ func TestRollupHoltWinters(t *testing.T) {
|
|||
func TestRollupNewRollupFuncSuccess(t *testing.T) {
|
||||
f := func(funcName string, vExpected float64) {
|
||||
t.Helper()
|
||||
var me promql.MetricExpr
|
||||
args := []interface{}{&promql.RollupExpr{Expr: &me}}
|
||||
var me metricsql.MetricExpr
|
||||
args := []interface{}{&metricsql.RollupExpr{Expr: &me}}
|
||||
testRollupFunc(t, funcName, args, &me, vExpected)
|
||||
}
|
||||
|
||||
|
@ -328,7 +329,7 @@ func TestRollupNewRollupFuncError(t *testing.T) {
|
|||
Values: []float64{321},
|
||||
Timestamps: []int64{123},
|
||||
}}
|
||||
me := &promql.MetricExpr{}
|
||||
me := &metricsql.MetricExpr{}
|
||||
f("holt_winters", []interface{}{123, 123, 321})
|
||||
f("holt_winters", []interface{}{me, 123, 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/decimal"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/metricsql"
|
||||
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||
"github.com/valyala/histogram"
|
||||
)
|
||||
|
@ -102,7 +102,7 @@ func getTransformFunc(s string) transformFunc {
|
|||
|
||||
type transformFuncArg struct {
|
||||
ec *EvalConfig
|
||||
fe *promql.FuncExpr
|
||||
fe *metricsql.FuncExpr
|
||||
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)
|
||||
keepMetricGroup := transformFuncsKeepMetricGroup[name]
|
||||
for _, ts := range arg {
|
||||
|
@ -151,12 +151,13 @@ func transformAbsent(tfa *transformFuncArg) ([]*timeseries, error) {
|
|||
// Copy tags from arg
|
||||
rvs := evalNumber(tfa.ec, 1)
|
||||
rv := rvs[0]
|
||||
me, ok := tfa.fe.Args[0].(*promql.MetricExpr)
|
||||
me, ok := tfa.fe.Args[0].(*metricsql.MetricExpr)
|
||||
if !ok {
|
||||
return rvs, nil
|
||||
}
|
||||
for i := range me.TagFilters {
|
||||
tf := &me.TagFilters[i]
|
||||
tfs := toTagFilters(me.LabelFilters)
|
||||
for i := range tfs {
|
||||
tf := &tfs[i]
|
||||
if len(tf.Key) == 0 {
|
||||
continue
|
||||
}
|
||||
|
@ -1020,7 +1021,7 @@ func transformLabelTransform(tfa *transformFuncArg) ([]*timeseries, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
r, err := compileRegexp(regex)
|
||||
r, err := metricsql.CompileRegexp(regex)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`cannot compile regex %q: %s`, regex, err)
|
||||
}
|
||||
|
@ -1049,7 +1050,7 @@ func transformLabelReplace(tfa *transformFuncArg) ([]*timeseries, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
r, err := compileRegexpAnchored(regex)
|
||||
r, err := metricsql.CompileRegexpAnchored(regex)
|
||||
if err != nil {
|
||||
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.
|
||||
// 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)
|
||||
if err != nil {
|
||||
n = nan
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package promql
|
||||
package metricsql
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
@ -18,7 +18,7 @@ var aggrFuncs = map[string]bool{
|
|||
"topk": true,
|
||||
"quantile": true,
|
||||
|
||||
// Extended PromQL funcs
|
||||
// MetricsQL extension funcs
|
||||
"median": true,
|
||||
"limitk": true,
|
||||
"distinct": true,
|
|
@ -1,4 +1,4 @@
|
|||
package promql
|
||||
package metricsql
|
||||
|
||||
import (
|
||||
"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 (
|
||||
"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 (
|
||||
"fmt"
|
||||
|
@ -220,7 +220,7 @@ func scanIdent(s string) string {
|
|||
}
|
||||
}
|
||||
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]
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ func toHex(n byte) byte {
|
|||
return 'a' + (n - 10)
|
||||
}
|
||||
|
||||
func appendEscapedIdent(dst, s []byte) []byte {
|
||||
func appendEscapedIdent(dst []byte, s string) []byte {
|
||||
for i := 0; i < len(s); i++ {
|
||||
ch := s[i]
|
||||
if isIdentChar(ch) {
|
|
@ -1,4 +1,4 @@
|
|||
package promql
|
||||
package metricsql
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
@ -28,7 +28,7 @@ func TestUnescapeIdent(t *testing.T) {
|
|||
func TestAppendEscapedIdent(t *testing.T) {
|
||||
f := func(s, resultExpected string) {
|
||||
t.Helper()
|
||||
result := appendEscapedIdent(nil, []byte(s))
|
||||
result := appendEscapedIdent(nil, s)
|
||||
if string(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 (
|
||||
"regexp"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testParser = &Parser{
|
||||
compileRegexpAnchored: compileRegexpAnchored,
|
||||
}
|
||||
|
||||
func compileRegexpAnchored(re string) (*regexp.Regexp, error) {
|
||||
reAnchored := "^(?:" + re + ")$"
|
||||
return regexp.Compile(reAnchored)
|
||||
}
|
||||
|
||||
func TestParsePromQLSuccess(t *testing.T) {
|
||||
func TestParseSuccess(t *testing.T) {
|
||||
another := func(s string, sExpected string) {
|
||||
t.Helper()
|
||||
|
||||
e, err := testParser.ParsePromQL(s)
|
||||
e, err := Parse(s)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error when parsing %q: %s", s, err)
|
||||
}
|
||||
|
@ -150,24 +140,72 @@ func TestParsePromQLSuccess(t *testing.T) {
|
|||
another(`-inF`, `-Inf`)
|
||||
|
||||
// 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`)
|
||||
same(`m + ignoring () n[5m]`)
|
||||
another(`M + IGNORING () N[5m]`, `M + ignoring () N[5m]`)
|
||||
same(`m + on (foo) n[5m]`)
|
||||
another(`m + ON (Foo) n[5m]`, `m + on (Foo) 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`)
|
||||
same(`m1 + on (foo) group_right () 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`)
|
||||
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{x="y"}`)
|
||||
same(`("foo"[3s] + bar{x="y"})[5m:3s] offset 10s`)
|
||||
same(`("foo"[3s] + bar{x="y"})[5i:3i] offset 10i`)
|
||||
same(`bar + "foo" offset 3s`)
|
||||
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
|
||||
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)`)
|
||||
same(`(foo, (bar, baz), ((x, y), (z, y), xx))`)
|
||||
another(`1+(foo, bar,)`, `1 + (foo, bar)`)
|
||||
another(`((foo(bar,baz)), (1+(2)+(3,4)+()))`, `(foo(bar, baz), (3 + (3, 4)) + ())`)
|
||||
same(`()`)
|
||||
|
||||
// funcExpr
|
||||
|
@ -192,7 +231,7 @@ func TestParsePromQLSuccess(t *testing.T) {
|
|||
same(`F(HttpServerRequest)`)
|
||||
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
|
||||
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 (s) (xx)[5s]`, `(sum(xx) by (s))[5s]`)
|
||||
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) by () or sum(b) without (x, y)`)
|
||||
same(`sum(a) + sum(b)`)
|
||||
|
@ -237,7 +278,7 @@ func TestParsePromQLSuccess(t *testing.T) {
|
|||
another(`with (foo = bar{x="x"}) "x"`, `"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"}) 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"}) sum(x)`, `sum(x)`)
|
||||
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(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, 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 (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"})`)
|
||||
|
@ -328,6 +370,18 @@ func TestParsePromQLSuccess(t *testing.T) {
|
|||
)
|
||||
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`)
|
||||
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 (
|
||||
commonFilters = {instance="foo"},
|
||||
timeToFuckup(currv, maxv) = (maxv - currv) / rate(currv)
|
||||
|
@ -341,15 +395,16 @@ func TestParsePromQLSuccess(t *testing.T) {
|
|||
)
|
||||
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))`)
|
||||
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) {
|
||||
t.Helper()
|
||||
|
||||
e, err := testParser.ParsePromQL(s)
|
||||
e, err := Parse(s)
|
||||
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 {
|
||||
t.Fatalf("expecting nil expr when parsing %q", s)
|
|
@ -1,4 +1,4 @@
|
|||
package promql
|
||||
package metricsql
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
@ -8,12 +8,14 @@ import (
|
|||
"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 + ")$"
|
||||
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)
|
||||
if rcv != nil {
|
||||
return rcv.r, rcv.err
|
|
@ -1,12 +1,10 @@
|
|||
package promql
|
||||
package metricsql
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var rollupFuncs = map[string]bool{
|
||||
"default_rollup": true, // default rollup func
|
||||
|
||||
// Standard rollup funcs from PromQL.
|
||||
// See funcs accepting range-vector on https://prometheus.io/docs/prometheus/latest/querying/functions/ .
|
||||
"changes": true,
|
||||
|
@ -30,6 +28,7 @@ var rollupFuncs = map[string]bool{
|
|||
"stdvar_over_time": true,
|
||||
|
||||
// Additional rollup funcs.
|
||||
"default_rollup": true,
|
||||
"sum2_over_time": true,
|
||||
"geomean_over_time": true,
|
||||
"first_over_time": true,
|
|
@ -1,4 +1,4 @@
|
|||
package promql
|
||||
package metricsql
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
@ -36,7 +36,7 @@ var transformFuncs = map[string]bool{
|
|||
"vector": true,
|
||||
"year": true,
|
||||
|
||||
// New funcs
|
||||
// New funcs from MetricsQL
|
||||
"label_set": true,
|
||||
"label_del": true,
|
||||
"label_keep": true,
|
||||
|
@ -45,7 +45,7 @@ var transformFuncs = map[string]bool{
|
|||
"label_transform": true,
|
||||
"label_value": true,
|
||||
"union": true,
|
||||
"": true,
|
||||
"": true, // empty func is a synonim to union
|
||||
"keep_last_value": true,
|
||||
"start": 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