mirror of https://github.com/prometheus/prometheus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4529 lines
107 KiB
4529 lines
107 KiB
// Copyright 2015 The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package parser
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/prometheus/common/model"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/prometheus/prometheus/model/histogram"
|
|
"github.com/prometheus/prometheus/model/labels"
|
|
"github.com/prometheus/prometheus/util/testutil"
|
|
|
|
"github.com/prometheus/prometheus/promql/parser/posrange"
|
|
)
|
|
|
|
var testExpr = []struct {
|
|
input string // The input to be parsed.
|
|
expected Expr // The expected expression AST.
|
|
fail bool // Whether parsing is supposed to fail.
|
|
errMsg string // If not empty the parsing error has to contain this string.
|
|
}{
|
|
// Scalars and scalar-to-scalar operations.
|
|
{
|
|
input: "1",
|
|
expected: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
},
|
|
{
|
|
input: "+Inf",
|
|
expected: &NumberLiteral{
|
|
Val: math.Inf(1),
|
|
PosRange: posrange.PositionRange{Start: 0, End: 4},
|
|
},
|
|
},
|
|
{
|
|
input: "-Inf",
|
|
expected: &NumberLiteral{
|
|
Val: math.Inf(-1),
|
|
PosRange: posrange.PositionRange{Start: 0, End: 4},
|
|
},
|
|
},
|
|
{
|
|
input: ".5",
|
|
expected: &NumberLiteral{
|
|
Val: 0.5,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 2},
|
|
},
|
|
},
|
|
{
|
|
input: "5.",
|
|
expected: &NumberLiteral{
|
|
Val: 5,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 2},
|
|
},
|
|
},
|
|
{
|
|
input: "123.4567",
|
|
expected: &NumberLiteral{
|
|
Val: 123.4567,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 8},
|
|
},
|
|
},
|
|
{
|
|
input: "5e-3",
|
|
expected: &NumberLiteral{
|
|
Val: 0.005,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 4},
|
|
},
|
|
},
|
|
{
|
|
input: "5e3",
|
|
expected: &NumberLiteral{
|
|
Val: 5000,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 3},
|
|
},
|
|
},
|
|
{
|
|
input: "0xc",
|
|
expected: &NumberLiteral{
|
|
Val: 12,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 3},
|
|
},
|
|
},
|
|
{
|
|
input: "0755",
|
|
expected: &NumberLiteral{
|
|
Val: 493,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 4},
|
|
},
|
|
},
|
|
{
|
|
input: "+5.5e-3",
|
|
expected: &NumberLiteral{
|
|
Val: 0.0055,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 7},
|
|
},
|
|
},
|
|
{
|
|
input: "-0755",
|
|
expected: &NumberLiteral{
|
|
Val: -493,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 5},
|
|
},
|
|
},
|
|
{
|
|
input: "1 + 1",
|
|
expected: &BinaryExpr{
|
|
Op: ADD,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 4, End: 5},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "1 - 1",
|
|
expected: &BinaryExpr{
|
|
Op: SUB,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 4, End: 5},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "1 * 1",
|
|
expected: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 4, End: 5},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "1 % 1",
|
|
expected: &BinaryExpr{
|
|
Op: MOD,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 4, End: 5},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "1 / 1",
|
|
expected: &BinaryExpr{
|
|
Op: DIV,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 4, End: 5},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "1 == bool 1",
|
|
expected: &BinaryExpr{
|
|
Op: EQLC,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 10, End: 11},
|
|
},
|
|
ReturnBool: true,
|
|
},
|
|
},
|
|
{
|
|
input: "1 != bool 1",
|
|
expected: &BinaryExpr{
|
|
Op: NEQ,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 10, End: 11},
|
|
},
|
|
ReturnBool: true,
|
|
},
|
|
},
|
|
{
|
|
input: "1 > bool 1",
|
|
expected: &BinaryExpr{
|
|
Op: GTR,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 9, End: 10},
|
|
},
|
|
ReturnBool: true,
|
|
},
|
|
},
|
|
{
|
|
input: "1 >= bool 1",
|
|
expected: &BinaryExpr{
|
|
Op: GTE,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 10, End: 11},
|
|
},
|
|
ReturnBool: true,
|
|
},
|
|
},
|
|
{
|
|
input: "1 < bool 1",
|
|
expected: &BinaryExpr{
|
|
Op: LSS,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 9, End: 10},
|
|
},
|
|
ReturnBool: true,
|
|
},
|
|
},
|
|
{
|
|
input: "1 <= bool 1",
|
|
expected: &BinaryExpr{
|
|
Op: LTE,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 10, End: 11},
|
|
},
|
|
ReturnBool: true,
|
|
},
|
|
},
|
|
{
|
|
input: "-1^2",
|
|
expected: &UnaryExpr{
|
|
Op: SUB,
|
|
Expr: &BinaryExpr{
|
|
Op: POW,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 1, End: 2},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 2,
|
|
PosRange: posrange.PositionRange{Start: 3, End: 4},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "-1*2",
|
|
expected: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &NumberLiteral{
|
|
Val: -1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 2},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 2,
|
|
PosRange: posrange.PositionRange{Start: 3, End: 4},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "-1+2",
|
|
expected: &BinaryExpr{
|
|
Op: ADD,
|
|
LHS: &NumberLiteral{
|
|
Val: -1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 2},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 2,
|
|
PosRange: posrange.PositionRange{Start: 3, End: 4},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "-1^-2",
|
|
expected: &UnaryExpr{
|
|
Op: SUB,
|
|
Expr: &BinaryExpr{
|
|
Op: POW,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 1, End: 2},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: -2,
|
|
PosRange: posrange.PositionRange{Start: 3, End: 5},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "+1 + -2 * 1",
|
|
expected: &BinaryExpr{
|
|
Op: ADD,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 2},
|
|
},
|
|
RHS: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &NumberLiteral{
|
|
Val: -2,
|
|
PosRange: posrange.PositionRange{Start: 5, End: 7},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 10, End: 11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "1 + 2/(3*1)",
|
|
expected: &BinaryExpr{
|
|
Op: ADD,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &BinaryExpr{
|
|
Op: DIV,
|
|
LHS: &NumberLiteral{
|
|
Val: 2,
|
|
PosRange: posrange.PositionRange{Start: 4, End: 5},
|
|
},
|
|
RHS: &ParenExpr{
|
|
Expr: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &NumberLiteral{
|
|
Val: 3,
|
|
PosRange: posrange.PositionRange{Start: 7, End: 8},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 9, End: 10},
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{Start: 6, End: 11},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "1 < bool 2 - 1 * 2",
|
|
expected: &BinaryExpr{
|
|
Op: LSS,
|
|
ReturnBool: true,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 1},
|
|
},
|
|
RHS: &BinaryExpr{
|
|
Op: SUB,
|
|
LHS: &NumberLiteral{
|
|
Val: 2,
|
|
PosRange: posrange.PositionRange{Start: 9, End: 10},
|
|
},
|
|
RHS: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 13, End: 14},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 2,
|
|
PosRange: posrange.PositionRange{Start: 17, End: 18},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "-some_metric",
|
|
expected: &UnaryExpr{
|
|
Op: SUB,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 1,
|
|
End: 12,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "+some_metric",
|
|
expected: &UnaryExpr{
|
|
Op: ADD,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 1,
|
|
End: 12,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: " +some_metric",
|
|
expected: &UnaryExpr{
|
|
Op: ADD,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 2,
|
|
End: 13,
|
|
},
|
|
},
|
|
StartPos: 1,
|
|
},
|
|
},
|
|
{
|
|
input: ` +{"some_metric"}`,
|
|
expected: &UnaryExpr{
|
|
Op: ADD,
|
|
Expr: &VectorSelector{
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 2,
|
|
End: 17,
|
|
},
|
|
},
|
|
StartPos: 1,
|
|
},
|
|
},
|
|
{
|
|
input: "",
|
|
fail: true,
|
|
errMsg: "no expression found in input",
|
|
},
|
|
{
|
|
input: "# just a comment\n\n",
|
|
fail: true,
|
|
errMsg: "no expression found in input",
|
|
},
|
|
{
|
|
input: "1+",
|
|
fail: true,
|
|
errMsg: "unexpected end of input",
|
|
},
|
|
{
|
|
input: ".",
|
|
fail: true,
|
|
errMsg: "unexpected character: '.'",
|
|
},
|
|
{
|
|
input: "2.5.",
|
|
fail: true,
|
|
errMsg: `1:1: parse error: bad number or duration syntax: "2.5."`,
|
|
},
|
|
{
|
|
input: "100..4",
|
|
fail: true,
|
|
errMsg: `1:1: parse error: bad number or duration syntax: "100.."`,
|
|
},
|
|
{
|
|
input: "0deadbeef",
|
|
fail: true,
|
|
errMsg: "bad number or duration syntax: \"0de\"",
|
|
},
|
|
{
|
|
input: "1 /",
|
|
fail: true,
|
|
errMsg: "unexpected end of input",
|
|
},
|
|
{
|
|
input: "*1",
|
|
fail: true,
|
|
errMsg: "unexpected <op:*>",
|
|
},
|
|
{
|
|
input: "(1))",
|
|
fail: true,
|
|
errMsg: "unexpected right parenthesis ')'",
|
|
},
|
|
{
|
|
input: "((1)",
|
|
fail: true,
|
|
errMsg: "unclosed left parenthesis",
|
|
},
|
|
{
|
|
input: "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999",
|
|
fail: true,
|
|
errMsg: "out of range",
|
|
},
|
|
{
|
|
input: "(",
|
|
fail: true,
|
|
errMsg: "unclosed left parenthesis",
|
|
},
|
|
{
|
|
input: "1 and 1",
|
|
fail: true,
|
|
errMsg: "set operator \"and\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "1 == 1",
|
|
fail: true,
|
|
errMsg: "1:3: parse error: comparisons between scalars must use BOOL modifier",
|
|
},
|
|
{
|
|
input: "1 or 1",
|
|
fail: true,
|
|
errMsg: "set operator \"or\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "1 unless 1",
|
|
fail: true,
|
|
errMsg: "set operator \"unless\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "1 !~ 1",
|
|
fail: true,
|
|
errMsg: `unexpected character after '!': '~'`,
|
|
},
|
|
{
|
|
input: "1 =~ 1",
|
|
fail: true,
|
|
errMsg: `unexpected character after '=': '~'`,
|
|
},
|
|
{
|
|
input: `-"string"`,
|
|
fail: true,
|
|
errMsg: `unary expression only allowed on expressions of type scalar or instant vector, got "string"`,
|
|
},
|
|
{
|
|
input: `-test[5m]`,
|
|
fail: true,
|
|
errMsg: `unary expression only allowed on expressions of type scalar or instant vector, got "range vector"`,
|
|
},
|
|
{
|
|
input: `*test`,
|
|
fail: true,
|
|
errMsg: "unexpected <op:*>",
|
|
},
|
|
{
|
|
input: "1 offset 1d",
|
|
fail: true,
|
|
errMsg: "1:1: parse error: offset modifier must be preceded by an instant vector selector or range vector selector or a subquery",
|
|
},
|
|
{
|
|
input: "foo offset 1s offset 2s",
|
|
fail: true,
|
|
errMsg: "offset may not be set multiple times",
|
|
},
|
|
{
|
|
input: "a - on(b) ignoring(c) d",
|
|
fail: true,
|
|
errMsg: "1:11: parse error: unexpected <ignoring>",
|
|
},
|
|
// Vector binary operations.
|
|
{
|
|
input: "foo * bar",
|
|
expected: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 6,
|
|
End: 9,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardOneToOne},
|
|
},
|
|
},
|
|
{
|
|
input: "foo * sum",
|
|
expected: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "sum",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "sum"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 6,
|
|
End: 9,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardOneToOne},
|
|
},
|
|
},
|
|
{
|
|
input: "foo == 1",
|
|
expected: &BinaryExpr{
|
|
Op: EQLC,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 7, End: 8},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo == bool 1",
|
|
expected: &BinaryExpr{
|
|
Op: EQLC,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &NumberLiteral{
|
|
Val: 1,
|
|
PosRange: posrange.PositionRange{Start: 12, End: 13},
|
|
},
|
|
ReturnBool: true,
|
|
},
|
|
},
|
|
{
|
|
input: "2.5 / bar",
|
|
expected: &BinaryExpr{
|
|
Op: DIV,
|
|
LHS: &NumberLiteral{
|
|
Val: 2.5,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 3},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 6,
|
|
End: 9,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo and bar",
|
|
expected: &BinaryExpr{
|
|
Op: LAND,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 8,
|
|
End: 11,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardManyToMany},
|
|
},
|
|
},
|
|
{
|
|
input: "foo or bar",
|
|
expected: &BinaryExpr{
|
|
Op: LOR,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 7,
|
|
End: 10,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardManyToMany},
|
|
},
|
|
},
|
|
{
|
|
input: "foo unless bar",
|
|
expected: &BinaryExpr{
|
|
Op: LUNLESS,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 11,
|
|
End: 14,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardManyToMany},
|
|
},
|
|
},
|
|
{
|
|
// Test and/or precedence and reassigning of operands.
|
|
input: "foo + bar or bla and blub",
|
|
expected: &BinaryExpr{
|
|
Op: LOR,
|
|
LHS: &BinaryExpr{
|
|
Op: ADD,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 6,
|
|
End: 9,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardOneToOne},
|
|
},
|
|
RHS: &BinaryExpr{
|
|
Op: LAND,
|
|
LHS: &VectorSelector{
|
|
Name: "bla",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bla"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 13,
|
|
End: 16,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "blub",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "blub"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 21,
|
|
End: 25,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardManyToMany},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardManyToMany},
|
|
},
|
|
},
|
|
{
|
|
// Test and/or/unless precedence.
|
|
input: "foo and bar unless baz or qux",
|
|
expected: &BinaryExpr{
|
|
Op: LOR,
|
|
LHS: &BinaryExpr{
|
|
Op: LUNLESS,
|
|
LHS: &BinaryExpr{
|
|
Op: LAND,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 8,
|
|
End: 11,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardManyToMany},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "baz",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 22,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardManyToMany},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "qux",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "qux"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 26,
|
|
End: 29,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{Card: CardManyToMany},
|
|
},
|
|
},
|
|
{
|
|
// Test precedence and reassigning of operands.
|
|
input: "bar + on(foo) bla / on(baz, buz) group_right(test) blub",
|
|
expected: &BinaryExpr{
|
|
Op: ADD,
|
|
LHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &BinaryExpr{
|
|
Op: DIV,
|
|
LHS: &VectorSelector{
|
|
Name: "bla",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bla"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 14,
|
|
End: 17,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "blub",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "blub"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 51,
|
|
End: 55,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardOneToMany,
|
|
MatchingLabels: []string{"baz", "buz"},
|
|
On: true,
|
|
Include: []string{"test"},
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardOneToOne,
|
|
MatchingLabels: []string{"foo"},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo * on(test,blub) bar",
|
|
expected: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 20,
|
|
End: 23,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardOneToOne,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo * on(test,blub) group_left bar",
|
|
expected: &BinaryExpr{
|
|
Op: MUL,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 31,
|
|
End: 34,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToOne,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo and on(test,blub) bar",
|
|
expected: &BinaryExpr{
|
|
Op: LAND,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 22,
|
|
End: 25,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToMany,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo and on() bar",
|
|
expected: &BinaryExpr{
|
|
Op: LAND,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 13,
|
|
End: 16,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToMany,
|
|
MatchingLabels: []string{},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo and ignoring(test,blub) bar",
|
|
expected: &BinaryExpr{
|
|
Op: LAND,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 28,
|
|
End: 31,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToMany,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo and ignoring() bar",
|
|
expected: &BinaryExpr{
|
|
Op: LAND,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 22,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToMany,
|
|
MatchingLabels: []string{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo unless on(bar) baz",
|
|
expected: &BinaryExpr{
|
|
Op: LUNLESS,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "baz",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 22,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToMany,
|
|
MatchingLabels: []string{"bar"},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo / on(test,blub) group_left(bar) bar",
|
|
expected: &BinaryExpr{
|
|
Op: DIV,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 36,
|
|
End: 39,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToOne,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
On: true,
|
|
Include: []string{"bar"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo / ignoring(test,blub) group_left(blub) bar",
|
|
expected: &BinaryExpr{
|
|
Op: DIV,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 43,
|
|
End: 46,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToOne,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
Include: []string{"blub"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo / ignoring(test,blub) group_left(bar) bar",
|
|
expected: &BinaryExpr{
|
|
Op: DIV,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 42,
|
|
End: 45,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToOne,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
Include: []string{"bar"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo - on(test,blub) group_right(bar,foo) bar",
|
|
expected: &BinaryExpr{
|
|
Op: SUB,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 41,
|
|
End: 44,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardOneToMany,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
Include: []string{"bar", "foo"},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo - ignoring(test,blub) group_right(bar,foo) bar",
|
|
expected: &BinaryExpr{
|
|
Op: SUB,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 47,
|
|
End: 50,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardOneToMany,
|
|
MatchingLabels: []string{"test", "blub"},
|
|
Include: []string{"bar", "foo"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo and 1",
|
|
fail: true,
|
|
errMsg: "set operator \"and\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "1 and foo",
|
|
fail: true,
|
|
errMsg: "set operator \"and\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "foo or 1",
|
|
fail: true,
|
|
errMsg: "set operator \"or\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "1 or foo",
|
|
fail: true,
|
|
errMsg: "set operator \"or\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "foo unless 1",
|
|
fail: true,
|
|
errMsg: "set operator \"unless\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "1 unless foo",
|
|
fail: true,
|
|
errMsg: "set operator \"unless\" not allowed in binary scalar expression",
|
|
},
|
|
{
|
|
input: "1 or on(bar) foo",
|
|
fail: true,
|
|
errMsg: "vector matching only allowed between instant vectors",
|
|
},
|
|
{
|
|
input: "foo == on(bar) 10",
|
|
fail: true,
|
|
errMsg: "vector matching only allowed between instant vectors",
|
|
},
|
|
{
|
|
input: "foo + group_left(baz) bar",
|
|
fail: true,
|
|
errMsg: "unexpected <group_left>",
|
|
},
|
|
{
|
|
input: "foo and on(bar) group_left(baz) bar",
|
|
fail: true,
|
|
errMsg: "no grouping allowed for \"and\" operation",
|
|
},
|
|
{
|
|
input: "foo and on(bar) group_right(baz) bar",
|
|
fail: true,
|
|
errMsg: "no grouping allowed for \"and\" operation",
|
|
},
|
|
{
|
|
input: "foo or on(bar) group_left(baz) bar",
|
|
fail: true,
|
|
errMsg: "no grouping allowed for \"or\" operation",
|
|
},
|
|
{
|
|
input: "foo or on(bar) group_right(baz) bar",
|
|
fail: true,
|
|
errMsg: "no grouping allowed for \"or\" operation",
|
|
},
|
|
{
|
|
input: "foo unless on(bar) group_left(baz) bar",
|
|
fail: true,
|
|
errMsg: "no grouping allowed for \"unless\" operation",
|
|
},
|
|
{
|
|
input: "foo unless on(bar) group_right(baz) bar",
|
|
fail: true,
|
|
errMsg: "no grouping allowed for \"unless\" operation",
|
|
},
|
|
{
|
|
input: `http_requests{group="production"} + on(instance) group_left(job,instance) cpu_count{type="smp"}`,
|
|
fail: true,
|
|
errMsg: "label \"instance\" must not occur in ON and GROUP clause at once",
|
|
},
|
|
{
|
|
input: "foo + bool bar",
|
|
fail: true,
|
|
errMsg: "bool modifier can only be used on comparison operators",
|
|
},
|
|
{
|
|
input: "foo + bool 10",
|
|
fail: true,
|
|
errMsg: "bool modifier can only be used on comparison operators",
|
|
},
|
|
{
|
|
input: "foo and bool 10",
|
|
fail: true,
|
|
errMsg: "bool modifier can only be used on comparison operators",
|
|
},
|
|
// Test Vector selector.
|
|
{
|
|
input: "foo",
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "min",
|
|
expected: &VectorSelector{
|
|
Name: "min",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "min"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo offset 5m",
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
OriginalOffset: 5 * time.Minute,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 13,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "foo offset -7m",
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
OriginalOffset: -7 * time.Minute,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 14,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo OFFSET 1h30m`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
OriginalOffset: 90 * time.Minute,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 16,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo OFFSET 1m30ms`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
OriginalOffset: time.Minute + 30*time.Millisecond,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 17,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ 1603774568`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(1603774568000),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 16,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ -100`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(-100000),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 10,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ .3`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(300),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 8,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ 3.`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(3000),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 8,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ 3.33`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(3330),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 10,
|
|
},
|
|
},
|
|
},
|
|
{ // Rounding off.
|
|
input: `foo @ 3.3333`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(3333),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 12,
|
|
},
|
|
},
|
|
},
|
|
{ // Rounding off.
|
|
input: `foo @ 3.3335`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(3334),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 12,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ 3e2`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(300000),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 9,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ 3e-1`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(300),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 10,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ 0xA`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(10000),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 9,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ -3.3e1`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(-33000),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 12,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ +Inf`,
|
|
fail: true,
|
|
errMsg: "1:1: parse error: timestamp out of bounds for @ modifier: +Inf",
|
|
},
|
|
{
|
|
input: `foo @ -Inf`,
|
|
fail: true,
|
|
errMsg: "1:1: parse error: timestamp out of bounds for @ modifier: -Inf",
|
|
},
|
|
{
|
|
input: `foo @ NaN`,
|
|
fail: true,
|
|
errMsg: "1:1: parse error: timestamp out of bounds for @ modifier: NaN",
|
|
},
|
|
{
|
|
input: fmt.Sprintf(`foo @ %f`, float64(math.MaxInt64)+1),
|
|
fail: true,
|
|
errMsg: fmt.Sprintf("1:1: parse error: timestamp out of bounds for @ modifier: %f", float64(math.MaxInt64)+1),
|
|
},
|
|
{
|
|
input: fmt.Sprintf(`foo @ %f`, float64(math.MinInt64)-1),
|
|
fail: true,
|
|
errMsg: fmt.Sprintf("1:1: parse error: timestamp out of bounds for @ modifier: %f", float64(math.MinInt64)-1),
|
|
},
|
|
{
|
|
input: `foo:bar{a="bc"}`,
|
|
expected: &VectorSelector{
|
|
Name: "foo:bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "a", "bc"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo:bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 15,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `{"foo"}`,
|
|
expected: &VectorSelector{
|
|
// When a metric is named inside the braces, the Name field is not set.
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 7,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `{'foo\'bar', 'a\\dos\\path'='boo\\urns'}`,
|
|
expected: &VectorSelector{
|
|
// When a metric is named inside the braces, the Name field is not set.
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, `foo'bar`),
|
|
MustLabelMatcher(labels.MatchEqual, `a\dos\path`, `boo\urns`),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 40,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `{'foo\'bar', ` + "`" + `a\dos\path` + "`" + `="boo"}`,
|
|
expected: &VectorSelector{
|
|
// When a metric is named inside the braces, the Name field is not set.
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, `foo'bar`),
|
|
MustLabelMatcher(labels.MatchEqual, `a\dos\path`, "boo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 32,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `{"foo", a="bc"}`,
|
|
expected: &VectorSelector{
|
|
// When a metric is named inside the braces, the Name field is not set.
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
MustLabelMatcher(labels.MatchEqual, "a", "bc"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 15,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo{NaN='bc'}`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "NaN", "bc"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 13,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo{bar='}'}`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "bar", "}"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 12,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo{a="b", foo!="bar", test=~"test", bar!~"baz"}`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "a", "b"),
|
|
MustLabelMatcher(labels.MatchNotEqual, "foo", "bar"),
|
|
MustLabelMatcher(labels.MatchRegexp, "test", "test"),
|
|
MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 48,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// Metric name in the middle of selector list is fine.
|
|
input: `{a="b", foo!="bar", "foo", test=~"test", bar!~"baz"}`,
|
|
expected: &VectorSelector{
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "a", "b"),
|
|
MustLabelMatcher(labels.MatchNotEqual, "foo", "bar"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
MustLabelMatcher(labels.MatchRegexp, "test", "test"),
|
|
MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 52,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo{a="b", foo!="bar", test=~"test", bar!~"baz",}`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "a", "b"),
|
|
MustLabelMatcher(labels.MatchNotEqual, "foo", "bar"),
|
|
MustLabelMatcher(labels.MatchRegexp, "test", "test"),
|
|
MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 49,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// Specifying __name__ twice inside the braces is ok.
|
|
input: `{__name__=~"bar", __name__!~"baz"}`,
|
|
expected: &VectorSelector{
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchRegexp, model.MetricNameLabel, "bar"),
|
|
MustLabelMatcher(labels.MatchNotRegexp, model.MetricNameLabel, "baz"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 34,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// Specifying __name__ with equality twice inside the braces is even allowed.
|
|
input: `{__name__="bar", __name__="baz"}`,
|
|
expected: &VectorSelector{
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 32,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// Because the above are allowed, this is also allowed.
|
|
input: `{"bar", __name__="baz"}`,
|
|
expected: &VectorSelector{
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 23,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `{`,
|
|
fail: true,
|
|
errMsg: "unexpected end of input inside braces",
|
|
},
|
|
{
|
|
input: `}`,
|
|
fail: true,
|
|
errMsg: "unexpected character: '}'",
|
|
},
|
|
{
|
|
input: `some{`,
|
|
fail: true,
|
|
errMsg: "unexpected end of input inside braces",
|
|
},
|
|
{
|
|
input: `some}`,
|
|
fail: true,
|
|
errMsg: "unexpected character: '}'",
|
|
},
|
|
{
|
|
input: `some_metric{a=b}`,
|
|
fail: true,
|
|
errMsg: "unexpected identifier \"b\" in label matching, expected string",
|
|
},
|
|
{
|
|
input: `some_metric{a:b="b"}`,
|
|
fail: true,
|
|
errMsg: "unexpected character inside braces: ':'",
|
|
},
|
|
{
|
|
input: `foo{a*"b"}`,
|
|
fail: true,
|
|
errMsg: "unexpected character inside braces: '*'",
|
|
},
|
|
{
|
|
input: `foo{a>="b"}`,
|
|
fail: true,
|
|
// TODO(fabxc): willingly lexing wrong tokens allows for more precise error
|
|
// messages from the parser - consider if this is an option.
|
|
errMsg: "unexpected character inside braces: '>'",
|
|
},
|
|
{
|
|
input: "some_metric{a=\"\xff\"}",
|
|
fail: true,
|
|
errMsg: "1:15: parse error: invalid UTF-8 rune",
|
|
},
|
|
{
|
|
input: `foo{gibberish}`,
|
|
fail: true,
|
|
errMsg: `unexpected "}" in label matching, expected label matching operator`,
|
|
},
|
|
{
|
|
input: `foo{1}`,
|
|
fail: true,
|
|
errMsg: "unexpected character inside braces: '1'",
|
|
},
|
|
{
|
|
input: `{}`,
|
|
fail: true,
|
|
errMsg: "vector selector must contain at least one non-empty matcher",
|
|
},
|
|
{
|
|
input: `{x=""}`,
|
|
fail: true,
|
|
errMsg: "vector selector must contain at least one non-empty matcher",
|
|
},
|
|
{
|
|
input: `{x=~".*"}`,
|
|
fail: true,
|
|
errMsg: "vector selector must contain at least one non-empty matcher",
|
|
},
|
|
{
|
|
input: `{x!~".+"}`,
|
|
fail: true,
|
|
errMsg: "vector selector must contain at least one non-empty matcher",
|
|
},
|
|
{
|
|
input: `{x!="a"}`,
|
|
fail: true,
|
|
errMsg: "vector selector must contain at least one non-empty matcher",
|
|
},
|
|
// Although {"bar", __name__="baz"} is allowed (see above), specifying a
|
|
// metric name inside and outside the braces is not.
|
|
{
|
|
input: `foo{__name__="bar"}`,
|
|
fail: true,
|
|
errMsg: `metric name must not be set twice: "foo" or "bar"`,
|
|
},
|
|
{
|
|
input: `foo{__name__= =}`,
|
|
fail: true,
|
|
errMsg: `1:15: parse error: unexpected "=" in label matching, expected string`,
|
|
},
|
|
{
|
|
input: `foo{,}`,
|
|
fail: true,
|
|
errMsg: `unexpected "," in label matching, expected identifier or "}"`,
|
|
},
|
|
{
|
|
input: `foo{__name__ == "bar"}`,
|
|
fail: true,
|
|
errMsg: `1:15: parse error: unexpected "=" in label matching, expected string`,
|
|
},
|
|
{
|
|
input: `foo{__name__="bar" lol}`,
|
|
fail: true,
|
|
errMsg: `unexpected identifier "lol" in label matching, expected "," or "}"`,
|
|
},
|
|
{
|
|
input: `foo{"a"=}`,
|
|
fail: true,
|
|
errMsg: `unexpected "}" in label matching, expected string`,
|
|
},
|
|
// Test matrix selector.
|
|
{
|
|
input: "test[5s]",
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 4,
|
|
},
|
|
},
|
|
Range: 5 * time.Second,
|
|
EndPos: 8,
|
|
},
|
|
},
|
|
{
|
|
input: "test[5m]",
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 4,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
EndPos: 8,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[5m30s]`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 5*time.Minute + 30*time.Second,
|
|
EndPos: 10,
|
|
},
|
|
},
|
|
{
|
|
input: "test[5h] OFFSET 5m",
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
OriginalOffset: 5 * time.Minute,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 4,
|
|
},
|
|
},
|
|
Range: 5 * time.Hour,
|
|
EndPos: 18,
|
|
},
|
|
},
|
|
{
|
|
input: "test[5d] OFFSET 10s",
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
OriginalOffset: 10 * time.Second,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 4,
|
|
},
|
|
},
|
|
Range: 5 * 24 * time.Hour,
|
|
EndPos: 19,
|
|
},
|
|
},
|
|
{
|
|
input: "test[5w] offset 2w",
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
OriginalOffset: 14 * 24 * time.Hour,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 4,
|
|
},
|
|
},
|
|
Range: 5 * 7 * 24 * time.Hour,
|
|
EndPos: 18,
|
|
},
|
|
},
|
|
{
|
|
input: `test{a="b"}[5y] OFFSET 3d`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
OriginalOffset: 3 * 24 * time.Hour,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "a", "b"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 11,
|
|
},
|
|
},
|
|
Range: 5 * 365 * 24 * time.Hour,
|
|
EndPos: 25,
|
|
},
|
|
},
|
|
{
|
|
input: `test{a="b"}[5m] OFFSET 3600`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
OriginalOffset: 1 * time.Hour,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "a", "b"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 11,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
EndPos: 27,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[3ms] @ 2.345`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(2345),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 3 * time.Millisecond,
|
|
EndPos: 16,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[4s180ms] @ 2.345`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(2345),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 4*time.Second + 180*time.Millisecond,
|
|
EndPos: 20,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[4.18] @ 2.345`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(2345),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 4*time.Second + 180*time.Millisecond,
|
|
EndPos: 17,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[4s18ms] @ 2.345`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(2345),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 4*time.Second + 18*time.Millisecond,
|
|
EndPos: 19,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[4.018] @ 2.345`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
Timestamp: makeInt64Pointer(2345),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 4*time.Second + 18*time.Millisecond,
|
|
EndPos: 18,
|
|
},
|
|
},
|
|
{
|
|
input: `test{a="b"}[5y] @ 1603774699`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
Timestamp: makeInt64Pointer(1603774699000),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "a", "b"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 11,
|
|
},
|
|
},
|
|
Range: 5 * 365 * 24 * time.Hour,
|
|
EndPos: 28,
|
|
},
|
|
},
|
|
{
|
|
input: "test[5]",
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 4,
|
|
},
|
|
},
|
|
Range: 5 * time.Second,
|
|
EndPos: 7,
|
|
},
|
|
},
|
|
{
|
|
input: `some_metric[5m] @ 1m`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "some_metric",
|
|
Timestamp: makeInt64Pointer(60000),
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 11,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
EndPos: 20,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[5mm]`,
|
|
fail: true,
|
|
errMsg: "bad number or duration syntax: \"5mm\"",
|
|
},
|
|
{
|
|
input: `foo[5m1]`,
|
|
fail: true,
|
|
errMsg: "bad number or duration syntax: \"5m1\"",
|
|
},
|
|
{
|
|
input: `foo[5m:1m1]`,
|
|
fail: true,
|
|
errMsg: "bad number or duration syntax: \"1m1\"",
|
|
},
|
|
{
|
|
input: `foo[5y1hs]`,
|
|
fail: true,
|
|
errMsg: "unknown unit \"hs\" in duration \"5y1hs\"",
|
|
},
|
|
{
|
|
input: `foo[5m1h]`,
|
|
fail: true,
|
|
errMsg: "not a valid duration string: \"5m1h\"",
|
|
},
|
|
{
|
|
input: `foo[5m1m]`,
|
|
fail: true,
|
|
errMsg: "not a valid duration string: \"5m1m\"",
|
|
},
|
|
{
|
|
input: `foo[0m]`,
|
|
fail: true,
|
|
errMsg: "duration must be greater than 0",
|
|
},
|
|
{
|
|
input: `foo["5m"]`,
|
|
fail: true,
|
|
},
|
|
{
|
|
input: `foo[]`,
|
|
fail: true,
|
|
errMsg: "bad number or duration syntax: \"\"",
|
|
},
|
|
{
|
|
input: `foo[-1]`,
|
|
fail: true,
|
|
errMsg: "bad number or duration syntax: \"\"",
|
|
},
|
|
{
|
|
input: `some_metric[5m] OFFSET 1mm`,
|
|
fail: true,
|
|
errMsg: "bad number or duration syntax: \"1mm\"",
|
|
},
|
|
{
|
|
input: `some_metric[5m] OFFSET`,
|
|
fail: true,
|
|
errMsg: "unexpected end of input in offset, expected number or duration",
|
|
},
|
|
{
|
|
input: `some_metric OFFSET 1m[5m]`,
|
|
fail: true,
|
|
errMsg: "1:22: parse error: no offset modifiers allowed before range",
|
|
},
|
|
{
|
|
input: `some_metric[5m] @`,
|
|
fail: true,
|
|
errMsg: "1:18: parse error: unexpected end of input in @, expected timestamp",
|
|
},
|
|
{
|
|
input: `some_metric @ 1234 [5m]`,
|
|
fail: true,
|
|
errMsg: "1:20: parse error: no @ modifiers allowed before range",
|
|
},
|
|
{
|
|
input: `(foo + bar)[5m]`,
|
|
fail: true,
|
|
errMsg: "1:12: parse error: ranges only allowed for vector selectors",
|
|
},
|
|
// Test aggregation.
|
|
{
|
|
input: "sum by (foo)(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 13,
|
|
End: 24,
|
|
},
|
|
},
|
|
Grouping: []string{"foo"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 25,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `sum by ("foo")({"some.metric"})`,
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Expr: &VectorSelector{
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some.metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 15,
|
|
End: 30,
|
|
},
|
|
},
|
|
Grouping: []string{"foo"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 31,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `sum by ("foo)(some_metric{})`,
|
|
fail: true,
|
|
errMsg: "unterminated quoted string",
|
|
},
|
|
{
|
|
input: `sum by ("foo", bar, 'baz')({"some.metric"})`,
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Expr: &VectorSelector{
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some.metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 27,
|
|
End: 42,
|
|
},
|
|
},
|
|
Grouping: []string{"foo", "bar", "baz"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 43,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "avg by (foo)(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: AVG,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 13,
|
|
End: 24,
|
|
},
|
|
},
|
|
Grouping: []string{"foo"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 25,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "max by (foo)(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: MAX,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 13,
|
|
End: 24,
|
|
},
|
|
},
|
|
Grouping: []string{"foo"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 25,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "sum without (foo) (some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Without: true,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 30,
|
|
},
|
|
},
|
|
Grouping: []string{"foo"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 31,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "sum (some_metric) without (foo)",
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Without: true,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 5,
|
|
End: 16,
|
|
},
|
|
},
|
|
Grouping: []string{"foo"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 31,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "stddev(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: STDDEV,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 7,
|
|
End: 18,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 19,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "stdvar by (foo)(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: STDVAR,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 16,
|
|
End: 27,
|
|
},
|
|
},
|
|
Grouping: []string{"foo"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 28,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "sum by ()(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 10,
|
|
End: 21,
|
|
},
|
|
},
|
|
Grouping: []string{},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 22,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "sum by (foo,bar,)(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 18,
|
|
End: 29,
|
|
},
|
|
},
|
|
Grouping: []string{"foo", "bar"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 30,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "sum by (foo,)(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 14,
|
|
End: 25,
|
|
},
|
|
},
|
|
Grouping: []string{"foo"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 26,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "topk(5, some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: TOPK,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 8,
|
|
End: 19,
|
|
},
|
|
},
|
|
Param: &NumberLiteral{
|
|
Val: 5,
|
|
PosRange: posrange.PositionRange{
|
|
Start: 5,
|
|
End: 6,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 20,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `count_values("value", some_metric)`,
|
|
expected: &AggregateExpr{
|
|
Op: COUNT_VALUES,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 22,
|
|
End: 33,
|
|
},
|
|
},
|
|
Param: &StringLiteral{
|
|
Val: "value",
|
|
PosRange: posrange.PositionRange{
|
|
Start: 13,
|
|
End: 20,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 34,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
// Test usage of keywords as label names.
|
|
input: "sum without(and, by, avg, count, alert, annotations)(some_metric)",
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Without: true,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 53,
|
|
End: 64,
|
|
},
|
|
},
|
|
Grouping: []string{"and", "by", "avg", "count", "alert", "annotations"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 65,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "sum without(==)(some_metric)",
|
|
fail: true,
|
|
errMsg: "unexpected <op:==> in grouping opts, expected label",
|
|
},
|
|
{
|
|
input: "sum without(,)(some_metric)",
|
|
fail: true,
|
|
errMsg: `unexpected "," in grouping opts, expected label`,
|
|
},
|
|
{
|
|
input: "sum without(foo,,)(some_metric)",
|
|
fail: true,
|
|
errMsg: `unexpected "," in grouping opts, expected label`,
|
|
},
|
|
{
|
|
input: `sum some_metric by (test)`,
|
|
fail: true,
|
|
errMsg: "unexpected identifier \"some_metric\"",
|
|
},
|
|
{
|
|
input: `sum (some_metric) by test`,
|
|
fail: true,
|
|
errMsg: "unexpected identifier \"test\" in grouping opts",
|
|
},
|
|
{
|
|
input: `sum (some_metric) by test`,
|
|
fail: true,
|
|
errMsg: "unexpected identifier \"test\" in grouping opts",
|
|
},
|
|
{
|
|
input: `sum () by (test)`,
|
|
fail: true,
|
|
errMsg: "no arguments for aggregate expression provided",
|
|
},
|
|
{
|
|
input: "MIN keep_common (some_metric)",
|
|
fail: true,
|
|
errMsg: "1:5: parse error: unexpected identifier \"keep_common\"",
|
|
},
|
|
{
|
|
input: "MIN (some_metric) keep_common",
|
|
fail: true,
|
|
errMsg: `unexpected identifier "keep_common"`,
|
|
},
|
|
{
|
|
input: `sum (some_metric) without (test) by (test)`,
|
|
fail: true,
|
|
errMsg: "unexpected <by>",
|
|
},
|
|
{
|
|
input: `sum without (test) (some_metric) by (test)`,
|
|
fail: true,
|
|
errMsg: "unexpected <by>",
|
|
},
|
|
{
|
|
input: `topk(some_metric)`,
|
|
fail: true,
|
|
errMsg: "wrong number of arguments for aggregate expression provided, expected 2, got 1",
|
|
},
|
|
{
|
|
input: `topk(some_metric,)`,
|
|
fail: true,
|
|
errMsg: "trailing commas not allowed in function call args",
|
|
},
|
|
{
|
|
input: `topk(some_metric, other_metric)`,
|
|
fail: true,
|
|
errMsg: "1:6: parse error: expected type scalar in aggregation parameter, got instant vector",
|
|
},
|
|
{
|
|
input: `count_values(5, other_metric)`,
|
|
fail: true,
|
|
errMsg: "1:14: parse error: expected type string in aggregation parameter, got scalar",
|
|
},
|
|
{
|
|
input: `rate(some_metric[5m]) @ 1234`,
|
|
fail: true,
|
|
errMsg: "1:1: parse error: @ modifier must be preceded by an instant vector selector or range vector selector or a subquery",
|
|
},
|
|
// Test function calls.
|
|
{
|
|
input: "time()",
|
|
expected: &Call{
|
|
Func: MustGetFunction("time"),
|
|
Args: Expressions{},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 6,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `floor(some_metric{foo!="bar"})`,
|
|
expected: &Call{
|
|
Func: MustGetFunction("floor"),
|
|
Args: Expressions{
|
|
&VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchNotEqual, "foo", "bar"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 6,
|
|
End: 29,
|
|
},
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 30,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "rate(some_metric[5m])",
|
|
expected: &Call{
|
|
Func: MustGetFunction("rate"),
|
|
Args: Expressions{
|
|
&MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 5,
|
|
End: 16,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
EndPos: 20,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 21,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "round(some_metric)",
|
|
expected: &Call{
|
|
Func: MustGetFunction("round"),
|
|
Args: Expressions{
|
|
&VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 6,
|
|
End: 17,
|
|
},
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 18,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "round(some_metric, 5)",
|
|
expected: &Call{
|
|
Func: MustGetFunction("round"),
|
|
Args: Expressions{
|
|
&VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 6,
|
|
End: 17,
|
|
},
|
|
},
|
|
&NumberLiteral{
|
|
Val: 5,
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 20,
|
|
},
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 21,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "floor()",
|
|
fail: true,
|
|
errMsg: "expected 1 argument(s) in call to \"floor\", got 0",
|
|
},
|
|
{
|
|
input: "floor(some_metric, other_metric)",
|
|
fail: true,
|
|
errMsg: "expected 1 argument(s) in call to \"floor\", got 2",
|
|
},
|
|
{
|
|
input: "floor(some_metric, 1)",
|
|
fail: true,
|
|
errMsg: "expected 1 argument(s) in call to \"floor\", got 2",
|
|
},
|
|
{
|
|
input: "floor(1)",
|
|
fail: true,
|
|
errMsg: "expected type instant vector in call to function \"floor\", got scalar",
|
|
},
|
|
{
|
|
input: "hour(some_metric, some_metric, some_metric)",
|
|
fail: true,
|
|
errMsg: "expected at most 1 argument(s) in call to \"hour\", got 3",
|
|
},
|
|
{
|
|
input: "time(some_metric)",
|
|
fail: true,
|
|
errMsg: "expected 0 argument(s) in call to \"time\", got 1",
|
|
},
|
|
{
|
|
input: "non_existent_function_far_bar()",
|
|
fail: true,
|
|
errMsg: "unknown function with name \"non_existent_function_far_bar\"",
|
|
},
|
|
{
|
|
input: "rate(some_metric)",
|
|
fail: true,
|
|
errMsg: "expected type range vector in call to function \"rate\", got instant vector",
|
|
},
|
|
{
|
|
input: "label_replace(a, `b`, `c\xff`, `d`, `.*`)",
|
|
fail: true,
|
|
errMsg: "1:23: parse error: invalid UTF-8 rune",
|
|
},
|
|
// Fuzzing regression tests.
|
|
{
|
|
input: "-=",
|
|
fail: true,
|
|
errMsg: `unexpected "="`,
|
|
},
|
|
{
|
|
input: "++-++-+-+-<",
|
|
fail: true,
|
|
errMsg: `unexpected <op:<>`,
|
|
},
|
|
{
|
|
input: "e-+=/(0)",
|
|
fail: true,
|
|
errMsg: `unexpected "="`,
|
|
},
|
|
{
|
|
input: "a>b()",
|
|
fail: true,
|
|
errMsg: `unknown function`,
|
|
},
|
|
{
|
|
input: "rate(avg)",
|
|
fail: true,
|
|
errMsg: `expected type range vector`,
|
|
},
|
|
{
|
|
// This is testing that we are not re-rendering the expression string for each error, which would timeout.
|
|
input: "(" + strings.Repeat("-{}-1", 10000) + ")" + strings.Repeat("[1m:]", 1000),
|
|
fail: true,
|
|
errMsg: `1:3: parse error: vector selector must contain at least one non-empty matcher`,
|
|
},
|
|
{
|
|
input: "sum(sum)",
|
|
expected: &AggregateExpr{
|
|
Op: SUM,
|
|
Expr: &VectorSelector{
|
|
Name: "sum",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "sum"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 4,
|
|
End: 7,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 8,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: "a + sum",
|
|
expected: &BinaryExpr{
|
|
Op: ADD,
|
|
LHS: &VectorSelector{
|
|
Name: "a",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "a"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 1,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "sum",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "sum"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 4,
|
|
End: 7,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{},
|
|
},
|
|
},
|
|
// String quoting and escape sequence interpretation tests.
|
|
{
|
|
input: `"double-quoted string \" with escaped quote"`,
|
|
expected: &StringLiteral{
|
|
Val: "double-quoted string \" with escaped quote",
|
|
PosRange: posrange.PositionRange{Start: 0, End: 44},
|
|
},
|
|
},
|
|
{
|
|
input: `'single-quoted string \' with escaped quote'`,
|
|
expected: &StringLiteral{
|
|
Val: "single-quoted string ' with escaped quote",
|
|
PosRange: posrange.PositionRange{Start: 0, End: 44},
|
|
},
|
|
},
|
|
{
|
|
input: "`backtick-quoted string`",
|
|
expected: &StringLiteral{
|
|
Val: "backtick-quoted string",
|
|
PosRange: posrange.PositionRange{Start: 0, End: 24},
|
|
},
|
|
},
|
|
{
|
|
input: `"\a\b\f\n\r\t\v\\\" - \xFF\377\u1234\U00010111\U0001011111☺"`,
|
|
expected: &StringLiteral{
|
|
Val: "\a\b\f\n\r\t\v\\\" - \xFF\377\u1234\U00010111\U0001011111☺",
|
|
PosRange: posrange.PositionRange{Start: 0, End: 62},
|
|
},
|
|
},
|
|
{
|
|
input: `'\a\b\f\n\r\t\v\\\' - \xFF\377\u1234\U00010111\U0001011111☺'`,
|
|
expected: &StringLiteral{
|
|
Val: "\a\b\f\n\r\t\v\\' - \xFF\377\u1234\U00010111\U0001011111☺",
|
|
PosRange: posrange.PositionRange{Start: 0, End: 62},
|
|
},
|
|
},
|
|
{
|
|
input: "`" + `\a\b\f\n\r\t\v\\\"\' - \xFF\377\u1234\U00010111\U0001011111☺` + "`",
|
|
expected: &StringLiteral{
|
|
Val: `\a\b\f\n\r\t\v\\\"\' - \xFF\377\u1234\U00010111\U0001011111☺`,
|
|
PosRange: posrange.PositionRange{Start: 0, End: 64},
|
|
},
|
|
},
|
|
{
|
|
input: "`\\``",
|
|
fail: true,
|
|
errMsg: "unterminated raw string",
|
|
},
|
|
{
|
|
input: `"\`,
|
|
fail: true,
|
|
errMsg: "escape sequence not terminated",
|
|
},
|
|
{
|
|
input: `"\c"`,
|
|
fail: true,
|
|
errMsg: "unknown escape sequence U+0063 'c'",
|
|
},
|
|
{
|
|
input: `"\x."`,
|
|
fail: true,
|
|
errMsg: "illegal character U+002E '.' in escape sequence",
|
|
},
|
|
// Subquery.
|
|
{
|
|
input: `foo{bar="baz"}[`,
|
|
fail: true,
|
|
errMsg: `1:16: parse error: bad number or duration syntax: ""`,
|
|
},
|
|
{
|
|
input: `foo{bar="baz"}[10m:6s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 14,
|
|
},
|
|
},
|
|
Range: 10 * time.Minute,
|
|
Step: 6 * time.Second,
|
|
EndPos: 22,
|
|
},
|
|
},
|
|
{
|
|
input: `foo{bar="baz"}[10m5s:1h6ms]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 14,
|
|
},
|
|
},
|
|
Range: 10*time.Minute + 5*time.Second,
|
|
Step: time.Hour + 6*time.Millisecond,
|
|
EndPos: 27,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[10m:]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 10 * time.Minute,
|
|
EndPos: 9,
|
|
},
|
|
},
|
|
{
|
|
input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:5s])`,
|
|
expected: &Call{
|
|
Func: MustGetFunction("min_over_time"),
|
|
Args: Expressions{
|
|
&SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("rate"),
|
|
Args: Expressions{
|
|
&MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 33,
|
|
},
|
|
},
|
|
Range: 2 * time.Second,
|
|
EndPos: 37,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 14,
|
|
End: 38,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
Step: 5 * time.Second,
|
|
|
|
EndPos: 45,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 46,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:])[4m:3s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("min_over_time"),
|
|
Args: Expressions{
|
|
&SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("rate"),
|
|
Args: Expressions{
|
|
&MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 33,
|
|
},
|
|
},
|
|
Range: 2 * time.Second,
|
|
EndPos: 37,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 14,
|
|
End: 38,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
EndPos: 43,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 44,
|
|
},
|
|
},
|
|
Range: 4 * time.Minute,
|
|
Step: 3 * time.Second,
|
|
EndPos: 51,
|
|
},
|
|
},
|
|
{
|
|
input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:] offset 4m)[4m:3s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("min_over_time"),
|
|
Args: Expressions{
|
|
&SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("rate"),
|
|
Args: Expressions{
|
|
&MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 33,
|
|
},
|
|
},
|
|
Range: 2 * time.Second,
|
|
EndPos: 37,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 14,
|
|
End: 38,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
OriginalOffset: 4 * time.Minute,
|
|
EndPos: 53,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 54,
|
|
},
|
|
},
|
|
Range: 4 * time.Minute,
|
|
Step: 3 * time.Second,
|
|
EndPos: 61,
|
|
},
|
|
},
|
|
{
|
|
input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:] @ 1603775091)[4m:3s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("min_over_time"),
|
|
Args: Expressions{
|
|
&SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("rate"),
|
|
Args: Expressions{
|
|
&MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 33,
|
|
},
|
|
},
|
|
Range: 2 * time.Second,
|
|
EndPos: 37,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 14,
|
|
End: 38,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
Timestamp: makeInt64Pointer(1603775091000),
|
|
EndPos: 56,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 57,
|
|
},
|
|
},
|
|
Range: 4 * time.Minute,
|
|
Step: 3 * time.Second,
|
|
EndPos: 64,
|
|
},
|
|
},
|
|
{
|
|
input: `min_over_time(rate(foo{bar="baz"}[2s])[5m:] @ -160377509)[4m:3s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("min_over_time"),
|
|
Args: Expressions{
|
|
&SubqueryExpr{
|
|
Expr: &Call{
|
|
Func: MustGetFunction("rate"),
|
|
Args: Expressions{
|
|
&MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "bar", "baz"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 33,
|
|
},
|
|
},
|
|
Range: 2 * time.Second,
|
|
EndPos: 37,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 14,
|
|
End: 38,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
Timestamp: makeInt64Pointer(-160377509000),
|
|
EndPos: 56,
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 57,
|
|
},
|
|
},
|
|
Range: 4 * time.Minute,
|
|
Step: 3 * time.Second,
|
|
EndPos: 64,
|
|
},
|
|
},
|
|
{
|
|
input: "sum without(and, by, avg, count, alert, annotations)(some_metric) [30m:10s]",
|
|
expected: &SubqueryExpr{
|
|
Expr: &AggregateExpr{
|
|
Op: SUM,
|
|
Without: true,
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 53,
|
|
End: 64,
|
|
},
|
|
},
|
|
Grouping: []string{"and", "by", "avg", "count", "alert", "annotations"},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 65,
|
|
},
|
|
},
|
|
Range: 30 * time.Minute,
|
|
Step: 10 * time.Second,
|
|
EndPos: 75,
|
|
},
|
|
},
|
|
{
|
|
input: `some_metric OFFSET 1m [10m:5s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 21,
|
|
},
|
|
OriginalOffset: 1 * time.Minute,
|
|
},
|
|
Range: 10 * time.Minute,
|
|
Step: 5 * time.Second,
|
|
EndPos: 30,
|
|
},
|
|
},
|
|
{
|
|
input: `some_metric @ 123 [10m:5s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 17,
|
|
},
|
|
Timestamp: makeInt64Pointer(123000),
|
|
},
|
|
Range: 10 * time.Minute,
|
|
Step: 5 * time.Second,
|
|
EndPos: 26,
|
|
},
|
|
},
|
|
{
|
|
input: `some_metric @ 123 offset 1m [10m:5s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 27,
|
|
},
|
|
Timestamp: makeInt64Pointer(123000),
|
|
OriginalOffset: 1 * time.Minute,
|
|
},
|
|
Range: 10 * time.Minute,
|
|
Step: 5 * time.Second,
|
|
EndPos: 36,
|
|
},
|
|
},
|
|
{
|
|
input: `some_metric offset 1m @ 123 [10m:5s]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 27,
|
|
},
|
|
Timestamp: makeInt64Pointer(123000),
|
|
OriginalOffset: 1 * time.Minute,
|
|
},
|
|
Range: 10 * time.Minute,
|
|
Step: 5 * time.Second,
|
|
EndPos: 36,
|
|
},
|
|
},
|
|
{
|
|
input: `some_metric[10m:5s] offset 1m @ 123`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "some_metric",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 11,
|
|
},
|
|
},
|
|
Timestamp: makeInt64Pointer(123000),
|
|
OriginalOffset: 1 * time.Minute,
|
|
Range: 10 * time.Minute,
|
|
Step: 5 * time.Second,
|
|
EndPos: 35,
|
|
},
|
|
},
|
|
{
|
|
input: `(foo + bar{nm="val"})[5m:]`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &ParenExpr{
|
|
Expr: &BinaryExpr{
|
|
Op: ADD,
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardOneToOne,
|
|
},
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 1,
|
|
End: 4,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "nm", "val"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 7,
|
|
End: 20,
|
|
},
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 21,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
EndPos: 26,
|
|
},
|
|
},
|
|
{
|
|
input: `(foo + bar{nm="val"})[5m:] offset 10m`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &ParenExpr{
|
|
Expr: &BinaryExpr{
|
|
Op: ADD,
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardOneToOne,
|
|
},
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 1,
|
|
End: 4,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "nm", "val"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 7,
|
|
End: 20,
|
|
},
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 21,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
OriginalOffset: 10 * time.Minute,
|
|
EndPos: 37,
|
|
},
|
|
},
|
|
{
|
|
input: `(foo + bar{nm="val"} @ 1234)[5m:] @ 1603775019`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &ParenExpr{
|
|
Expr: &BinaryExpr{
|
|
Op: ADD,
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardOneToOne,
|
|
},
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 1,
|
|
End: 4,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "nm", "val"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
Timestamp: makeInt64Pointer(1234000),
|
|
PosRange: posrange.PositionRange{
|
|
Start: 7,
|
|
End: 27,
|
|
},
|
|
},
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 28,
|
|
},
|
|
},
|
|
Range: 5 * time.Minute,
|
|
Timestamp: makeInt64Pointer(1603775019000),
|
|
EndPos: 46,
|
|
},
|
|
},
|
|
{
|
|
input: "test[5d] OFFSET 10s [10m:5s]",
|
|
fail: true,
|
|
errMsg: "1:1: parse error: subquery is only allowed on instant vector, got matrix",
|
|
},
|
|
{
|
|
input: `(foo + bar{nm="val"})[5m:][10m:5s]`,
|
|
fail: true,
|
|
errMsg: `1:1: parse error: subquery is only allowed on instant vector, got matrix`,
|
|
},
|
|
{
|
|
input: "rate(food[1m])[1h] offset 1h",
|
|
fail: true,
|
|
errMsg: `1:15: parse error: ranges only allowed for vector selectors`,
|
|
},
|
|
{
|
|
input: "rate(food[1m])[1h] @ 100",
|
|
fail: true,
|
|
errMsg: `1:15: parse error: ranges only allowed for vector selectors`,
|
|
},
|
|
// Preprocessors.
|
|
{
|
|
input: `foo @ start()`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
StartOrEnd: START,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 13,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo @ end()`,
|
|
expected: &VectorSelector{
|
|
Name: "foo",
|
|
StartOrEnd: END,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 11,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `test[5y] @ start()`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
StartOrEnd: START,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 4,
|
|
},
|
|
},
|
|
Range: 5 * 365 * 24 * time.Hour,
|
|
EndPos: 18,
|
|
},
|
|
},
|
|
{
|
|
input: `test[5y] @ end()`,
|
|
expected: &MatrixSelector{
|
|
VectorSelector: &VectorSelector{
|
|
Name: "test",
|
|
StartOrEnd: END,
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 4,
|
|
},
|
|
},
|
|
Range: 5 * 365 * 24 * time.Hour,
|
|
EndPos: 16,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[10m:6s] @ start()`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 10 * time.Minute,
|
|
Step: 6 * time.Second,
|
|
StartOrEnd: START,
|
|
EndPos: 21,
|
|
},
|
|
},
|
|
{
|
|
input: `foo[10m:6s] @ end()`,
|
|
expected: &SubqueryExpr{
|
|
Expr: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
Range: 10 * time.Minute,
|
|
Step: 6 * time.Second,
|
|
StartOrEnd: END,
|
|
EndPos: 19,
|
|
},
|
|
},
|
|
{
|
|
input: `start()`,
|
|
fail: true,
|
|
errMsg: `1:6: parse error: unexpected "("`,
|
|
},
|
|
{
|
|
input: `end()`,
|
|
fail: true,
|
|
errMsg: `1:4: parse error: unexpected "("`,
|
|
},
|
|
// Check that start and end functions do not mask metrics.
|
|
{
|
|
input: `start`,
|
|
expected: &VectorSelector{
|
|
Name: "start",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "start"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 5,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `end`,
|
|
expected: &VectorSelector{
|
|
Name: "end",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "end"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `start{end="foo"}`,
|
|
expected: &VectorSelector{
|
|
Name: "start",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "end", "foo"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "start"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 16,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `end{start="foo"}`,
|
|
expected: &VectorSelector{
|
|
Name: "end",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, "start", "foo"),
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "end"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 16,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo unless on(start) bar`,
|
|
expected: &BinaryExpr{
|
|
Op: LUNLESS,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 21,
|
|
End: 24,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToMany,
|
|
MatchingLabels: []string{"start"},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
input: `foo unless on(end) bar`,
|
|
expected: &BinaryExpr{
|
|
Op: LUNLESS,
|
|
LHS: &VectorSelector{
|
|
Name: "foo",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 0,
|
|
End: 3,
|
|
},
|
|
},
|
|
RHS: &VectorSelector{
|
|
Name: "bar",
|
|
LabelMatchers: []*labels.Matcher{
|
|
MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"),
|
|
},
|
|
PosRange: posrange.PositionRange{
|
|
Start: 19,
|
|
End: 22,
|
|
},
|
|
},
|
|
VectorMatching: &VectorMatching{
|
|
Card: CardManyToMany,
|
|
MatchingLabels: []string{"end"},
|
|
On: true,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func makeInt64Pointer(val int64) *int64 {
|
|
valp := new(int64)
|
|
*valp = val
|
|
return valp
|
|
}
|
|
|
|
func readable(s string) string {
|
|
const maxReadableStringLen = 40
|
|
if len(s) < maxReadableStringLen {
|
|
return s
|
|
}
|
|
return s[:maxReadableStringLen] + "..."
|
|
}
|
|
|
|
func TestParseExpressions(t *testing.T) {
|
|
model.NameValidationScheme = model.UTF8Validation
|
|
for _, test := range testExpr {
|
|
t.Run(readable(test.input), func(t *testing.T) {
|
|
expr, err := ParseExpr(test.input)
|
|
|
|
// Unexpected errors are always caused by a bug.
|
|
require.NotEqual(t, err, errUnexpected, "unexpected error occurred")
|
|
|
|
if !test.fail {
|
|
require.NoError(t, err)
|
|
expected := test.expected
|
|
|
|
// The FastRegexMatcher is not comparable with a deep equal, so only compare its String() version.
|
|
if actualVector, ok := expr.(*VectorSelector); ok {
|
|
require.IsType(t, &VectorSelector{}, test.expected, "error on input '%s'", test.input)
|
|
expectedVector := test.expected.(*VectorSelector)
|
|
|
|
require.Len(t, actualVector.LabelMatchers, len(expectedVector.LabelMatchers), "error on input '%s'", test.input)
|
|
|
|
for i := 0; i < len(actualVector.LabelMatchers); i++ {
|
|
expectedMatcher := expectedVector.LabelMatchers[i].String()
|
|
actualMatcher := actualVector.LabelMatchers[i].String()
|
|
|
|
require.Equal(t, expectedMatcher, actualMatcher, "unexpected label matcher '%s' on input '%s'", actualMatcher, test.input)
|
|
}
|
|
|
|
// Make a shallow copy of the expected expr (because the test cases are defined in a global variable)
|
|
// and then reset the LabelMatcher to not compared them with the following deep equal.
|
|
expectedCopy := *expectedVector
|
|
expectedCopy.LabelMatchers = nil
|
|
expected = &expectedCopy
|
|
actualVector.LabelMatchers = nil
|
|
}
|
|
|
|
require.Equal(t, expected, expr, "error on input '%s'", test.input)
|
|
} else {
|
|
require.ErrorContains(t, err, test.errMsg, "unexpected error on input '%s', expected '%s', got '%s'", test.input, test.errMsg, err.Error())
|
|
|
|
var errorList ParseErrors
|
|
ok := errors.As(err, &errorList)
|
|
|
|
require.True(t, ok, "unexpected error type")
|
|
|
|
for _, e := range errorList {
|
|
require.LessOrEqual(t, 0, e.PositionRange.Start, "parse error has negative position\nExpression '%s'\nError: %v", test.input, e)
|
|
require.LessOrEqual(t, e.PositionRange.Start, e.PositionRange.End, "parse error has negative length\nExpression '%s'\nError: %v", test.input, e)
|
|
require.LessOrEqual(t, e.PositionRange.End, posrange.Pos(len(test.input)), "parse error is not contained in input\nExpression '%s'\nError: %v", test.input, e)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// NaN has no equality. Thus, we need a separate test for it.
|
|
func TestNaNExpression(t *testing.T) {
|
|
expr, err := ParseExpr("NaN")
|
|
require.NoError(t, err)
|
|
|
|
nl, ok := expr.(*NumberLiteral)
|
|
require.True(t, ok, "expected number literal but got %T", expr)
|
|
require.True(t, math.IsNaN(nl.Val), "expected 'NaN' in number literal but got %v", nl.Val)
|
|
}
|
|
|
|
var testSeries = []struct {
|
|
input string
|
|
expectedMetric labels.Labels
|
|
expectedValues []SequenceValue
|
|
fail bool
|
|
}{
|
|
{
|
|
input: `{} 1 2 3`,
|
|
expectedMetric: labels.EmptyLabels(),
|
|
expectedValues: newSeq(1, 2, 3),
|
|
}, {
|
|
input: `{a="b"} -1 2 3`,
|
|
expectedMetric: labels.FromStrings("a", "b"),
|
|
expectedValues: newSeq(-1, 2, 3),
|
|
}, {
|
|
input: `my_metric 1 2 3`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric"),
|
|
expectedValues: newSeq(1, 2, 3),
|
|
}, {
|
|
input: `my_metric{} 1 2 3`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric"),
|
|
expectedValues: newSeq(1, 2, 3),
|
|
}, {
|
|
input: `my_metric{a="b"} 1 2 3`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, 2, 3),
|
|
}, {
|
|
input: `my_metric{a="b"} 1 2 3-10x4`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, 2, 3, -7, -17, -27, -37),
|
|
}, {
|
|
input: `my_metric{a="b"} 1 2 3-0x4`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, 2, 3, 3, 3, 3, 3),
|
|
}, {
|
|
input: `{} 1+1`,
|
|
fail: true,
|
|
}, {
|
|
input: `{} 1x0`,
|
|
expectedMetric: labels.EmptyLabels(),
|
|
expectedValues: newSeq(1),
|
|
}, {
|
|
input: `{} 1+1x0`,
|
|
expectedMetric: labels.EmptyLabels(),
|
|
expectedValues: newSeq(1),
|
|
}, {
|
|
input: `my_metric{a="b"} 1 3 _ 5 _x4`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, 3, none, 5, none, none, none, none),
|
|
}, {
|
|
input: `my_metric{a="b"} 1 3 _ 5 _a4`,
|
|
fail: true,
|
|
}, {
|
|
input: `my_metric{a="b"} 1 -1`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, -1),
|
|
}, {
|
|
input: `my_metric{a="b"} 1 +1`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, 1),
|
|
}, {
|
|
input: `my_metric{a="b"} 1 -1 -3-10x4 7 9 +5`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, -1, -3, -13, -23, -33, -43, 7, 9, 5),
|
|
}, {
|
|
input: `my_metric{a="b"} 1 +1 +4 -6 -2 8`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, 1, 4, -6, -2, 8),
|
|
}, {
|
|
// Trailing spaces should be correctly handles.
|
|
input: `my_metric{a="b"} 1 2 3 `,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"),
|
|
expectedValues: newSeq(1, 2, 3),
|
|
}, {
|
|
// Handle escaped unicode characters as whole label values.
|
|
input: `my_metric{a="\u70ac"} 1 2 3`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", `炬`),
|
|
expectedValues: newSeq(1, 2, 3),
|
|
}, {
|
|
// Handle escaped unicode characters as partial label values.
|
|
input: `my_metric{a="\u70ac = torch"} 1 2 3`,
|
|
expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", `炬 = torch`),
|
|
expectedValues: newSeq(1, 2, 3),
|
|
}, {
|
|
input: `my_metric{a="b"} -3-3 -3`,
|
|
fail: true,
|
|
}, {
|
|
input: `my_metric{a="b"} -3 -3-3`,
|
|
fail: true,
|
|
}, {
|
|
input: `my_metric{a="b"} -3 _-2`,
|
|
fail: true,
|
|
}, {
|
|
input: `my_metric{a="b"} -3 3+3x4-4`,
|
|
fail: true,
|
|
},
|
|
}
|
|
|
|
// For these tests only, we use the smallest float64 to signal an omitted value.
|
|
const none = math.SmallestNonzeroFloat64
|
|
|
|
func newSeq(vals ...float64) (res []SequenceValue) {
|
|
for _, v := range vals {
|
|
if v == none {
|
|
res = append(res, SequenceValue{Omitted: true})
|
|
} else {
|
|
res = append(res, SequenceValue{Value: v})
|
|
}
|
|
}
|
|
return res
|
|
}
|
|
|
|
func TestParseHistogramSeries(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
input string
|
|
expected []histogram.FloatHistogram
|
|
expectedError string
|
|
}{
|
|
{
|
|
name: "empty histogram",
|
|
input: "{} {{}}",
|
|
expected: []histogram.FloatHistogram{{}},
|
|
},
|
|
{
|
|
name: "empty histogram with space",
|
|
input: "{} {{ }}",
|
|
expected: []histogram.FloatHistogram{{}},
|
|
},
|
|
{
|
|
name: "all properties used",
|
|
input: `{} {{schema:1 sum:0.3 count:3.1 z_bucket:7.1 z_bucket_w:0.05 buckets:[5.1 10 7] offset:3 n_buckets:[4.1 5] n_offset:5 counter_reset_hint:gauge}}`,
|
|
expected: []histogram.FloatHistogram{{
|
|
Schema: 1,
|
|
Sum: 0.3,
|
|
Count: 3.1,
|
|
ZeroCount: 7.1,
|
|
ZeroThreshold: 0.05,
|
|
PositiveBuckets: []float64{5.1, 10, 7},
|
|
PositiveSpans: []histogram.Span{{Offset: 3, Length: 3}},
|
|
NegativeBuckets: []float64{4.1, 5},
|
|
NegativeSpans: []histogram.Span{{Offset: 5, Length: 2}},
|
|
CounterResetHint: histogram.GaugeType,
|
|
}},
|
|
},
|
|
{
|
|
name: "all properties used - with spaces",
|
|
input: `{} {{schema:1 sum:0.3 count:3 z_bucket:7 z_bucket_w:5 buckets:[5 10 7 ] offset:-3 n_buckets:[4 5] n_offset:5 counter_reset_hint:gauge }}`,
|
|
expected: []histogram.FloatHistogram{{
|
|
Schema: 1,
|
|
Sum: 0.3,
|
|
Count: 3,
|
|
ZeroCount: 7,
|
|
ZeroThreshold: 5,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{Offset: -3, Length: 3}},
|
|
NegativeBuckets: []float64{4, 5},
|
|
NegativeSpans: []histogram.Span{{Offset: 5, Length: 2}},
|
|
CounterResetHint: histogram.GaugeType,
|
|
}},
|
|
},
|
|
{
|
|
name: "all properties used, with negative values where supported",
|
|
input: `{} {{schema:1 sum:-0.3 count:-3.1 z_bucket:-7.1 z_bucket_w:0.05 buckets:[-5.1 -10 -7] offset:-3 n_buckets:[-4.1 -5] n_offset:-5 counter_reset_hint:gauge}}`,
|
|
expected: []histogram.FloatHistogram{{
|
|
Schema: 1,
|
|
Sum: -0.3,
|
|
Count: -3.1,
|
|
ZeroCount: -7.1,
|
|
ZeroThreshold: 0.05,
|
|
PositiveBuckets: []float64{-5.1, -10, -7},
|
|
PositiveSpans: []histogram.Span{{Offset: -3, Length: 3}},
|
|
NegativeBuckets: []float64{-4.1, -5},
|
|
NegativeSpans: []histogram.Span{{Offset: -5, Length: 2}},
|
|
CounterResetHint: histogram.GaugeType,
|
|
}},
|
|
},
|
|
{
|
|
name: "static series",
|
|
input: `{} {{buckets:[5 10 7] schema:1}}x2`,
|
|
expected: []histogram.FloatHistogram{
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "static series - x0",
|
|
input: `{} {{buckets:[5 10 7] schema:1}}x0`,
|
|
expected: []histogram.FloatHistogram{
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "2 histograms stated explicitly",
|
|
input: `{} {{buckets:[5 10 7] schema:1}} {{buckets:[1 2 3] schema:1}}`,
|
|
expected: []histogram.FloatHistogram{
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{1, 2, 3},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "series with increment - with different schemas",
|
|
input: `{} {{buckets:[5] schema:0}}+{{buckets:[1 2] schema:1}}x2`,
|
|
expected: []histogram.FloatHistogram{
|
|
{
|
|
PositiveBuckets: []float64{5},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 1,
|
|
}},
|
|
},
|
|
{
|
|
PositiveBuckets: []float64{6, 2},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 2,
|
|
}},
|
|
},
|
|
{
|
|
PositiveBuckets: []float64{7, 4},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 2,
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "series with decrement",
|
|
input: `{} {{buckets:[5 10 7] schema:1}}-{{buckets:[1 2 3] schema:1}}x2`,
|
|
expected: []histogram.FloatHistogram{
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{4, 8, 4},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{3, 6, 1},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "series with increment - 0x",
|
|
input: `{} {{buckets:[5 10 7] schema:1}}+{{buckets:[1 2 3] schema:1}}x0`,
|
|
expected: []histogram.FloatHistogram{
|
|
{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "series with different schemas - second one is smaller",
|
|
input: `{} {{buckets:[5 10 7] schema:1}}+{{buckets:[1 2 3] schema:0}}x2`,
|
|
expectedError: `1:63: parse error: error combining histograms: cannot merge from schema 0 to 1`,
|
|
},
|
|
{
|
|
name: "different order",
|
|
input: `{} {{buckets:[5 10 7] schema:1}}`,
|
|
expected: []histogram.FloatHistogram{{
|
|
Schema: 1,
|
|
PositiveBuckets: []float64{5, 10, 7},
|
|
PositiveSpans: []histogram.Span{{
|
|
Offset: 0,
|
|
Length: 3,
|
|
}},
|
|
}},
|
|
},
|
|
{
|
|
name: "double property",
|
|
input: `{} {{schema:1 schema:1}}`,
|
|
expectedError: `1:1: parse error: duplicate key "schema" in histogram`,
|
|
},
|
|
{
|
|
name: "unknown property",
|
|
input: `{} {{foo:1}}`,
|
|
expectedError: `1:6: parse error: bad histogram descriptor found: "foo"`,
|
|
},
|
|
{
|
|
name: "space before :",
|
|
input: `{} {{schema :1}}`,
|
|
expectedError: "1:6: parse error: missing `:` for histogram descriptor",
|
|
},
|
|
{
|
|
name: "space after :",
|
|
input: `{} {{schema: 1}}`,
|
|
expectedError: `1:13: parse error: unexpected " " in series values`,
|
|
},
|
|
{
|
|
name: "space after [",
|
|
input: `{} {{buckets:[ 1]}}`,
|
|
expectedError: `1:15: parse error: unexpected " " in series values`,
|
|
},
|
|
{
|
|
name: "space after {{",
|
|
input: `{} {{ schema:1}}`,
|
|
expectedError: `1:7: parse error: unexpected "<Item 57372>" "schema" in series values`,
|
|
},
|
|
{
|
|
name: "invalid counter reset hint value",
|
|
input: `{} {{counter_reset_hint:foo}}`,
|
|
expectedError: `1:25: parse error: bad histogram descriptor found: "foo"`,
|
|
},
|
|
{
|
|
name: "'unknown' counter reset hint value",
|
|
input: `{} {{counter_reset_hint:unknown}}`,
|
|
expected: []histogram.FloatHistogram{{
|
|
CounterResetHint: histogram.UnknownCounterReset,
|
|
}},
|
|
},
|
|
{
|
|
name: "'reset' counter reset hint value",
|
|
input: `{} {{counter_reset_hint:reset}}`,
|
|
expected: []histogram.FloatHistogram{{
|
|
CounterResetHint: histogram.CounterReset,
|
|
}},
|
|
},
|
|
{
|
|
name: "'not_reset' counter reset hint value",
|
|
input: `{} {{counter_reset_hint:not_reset}}`,
|
|
expected: []histogram.FloatHistogram{{
|
|
CounterResetHint: histogram.NotCounterReset,
|
|
}},
|
|
},
|
|
{
|
|
name: "'gauge' counter reset hint value",
|
|
input: `{} {{counter_reset_hint:gauge}}`,
|
|
expected: []histogram.FloatHistogram{{
|
|
CounterResetHint: histogram.GaugeType,
|
|
}},
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
_, vals, err := ParseSeriesDesc(test.input)
|
|
if test.expectedError != "" {
|
|
require.EqualError(t, err, test.expectedError)
|
|
return
|
|
}
|
|
require.NoError(t, err)
|
|
var got []histogram.FloatHistogram
|
|
for _, v := range vals {
|
|
got = append(got, *v.Histogram)
|
|
}
|
|
require.Equal(t, test.expected, got)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestHistogramTestExpression(t *testing.T) {
|
|
for _, test := range []struct {
|
|
name string
|
|
input histogram.FloatHistogram
|
|
expected string
|
|
}{
|
|
{
|
|
name: "single positive and negative span",
|
|
input: histogram.FloatHistogram{
|
|
Schema: 1,
|
|
Sum: -0.3,
|
|
Count: 3.1,
|
|
ZeroCount: 7.1,
|
|
ZeroThreshold: 0.05,
|
|
PositiveBuckets: []float64{5.1, 10, 7},
|
|
PositiveSpans: []histogram.Span{{Offset: -3, Length: 3}},
|
|
NegativeBuckets: []float64{4.1, 5},
|
|
NegativeSpans: []histogram.Span{{Offset: -5, Length: 2}},
|
|
},
|
|
expected: `{{schema:1 count:3.1 sum:-0.3 z_bucket:7.1 z_bucket_w:0.05 offset:-3 buckets:[5.1 10 7] n_offset:-5 n_buckets:[4.1 5]}}`,
|
|
},
|
|
{
|
|
name: "multiple positive and negative spans",
|
|
input: histogram.FloatHistogram{
|
|
PositiveBuckets: []float64{5.1, 10, 7},
|
|
PositiveSpans: []histogram.Span{
|
|
{Offset: -3, Length: 1},
|
|
{Offset: 4, Length: 2},
|
|
},
|
|
NegativeBuckets: []float64{4.1, 5, 7, 8, 9},
|
|
NegativeSpans: []histogram.Span{
|
|
{Offset: -1, Length: 2},
|
|
{Offset: 2, Length: 3},
|
|
},
|
|
},
|
|
expected: `{{offset:-3 buckets:[5.1 0 0 0 0 10 7] n_offset:-1 n_buckets:[4.1 5 0 0 7 8 9]}}`,
|
|
},
|
|
{
|
|
name: "known counter reset hint",
|
|
input: histogram.FloatHistogram{
|
|
Schema: 1,
|
|
Sum: -0.3,
|
|
Count: 3.1,
|
|
ZeroCount: 7.1,
|
|
ZeroThreshold: 0.05,
|
|
PositiveBuckets: []float64{5.1, 10, 7},
|
|
PositiveSpans: []histogram.Span{{Offset: -3, Length: 3}},
|
|
NegativeBuckets: []float64{4.1, 5},
|
|
NegativeSpans: []histogram.Span{{Offset: -5, Length: 2}},
|
|
CounterResetHint: histogram.CounterReset,
|
|
},
|
|
expected: `{{schema:1 count:3.1 sum:-0.3 z_bucket:7.1 z_bucket_w:0.05 counter_reset_hint:reset offset:-3 buckets:[5.1 10 7] n_offset:-5 n_buckets:[4.1 5]}}`,
|
|
},
|
|
} {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
expression := test.input.TestExpression()
|
|
require.Equal(t, test.expected, expression)
|
|
_, vals, err := ParseSeriesDesc("{} " + expression)
|
|
require.NoError(t, err)
|
|
require.Len(t, vals, 1)
|
|
canonical := vals[0].Histogram
|
|
require.NotNil(t, canonical)
|
|
require.Equal(t, test.expected, canonical.TestExpression())
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseSeries(t *testing.T) {
|
|
for _, test := range testSeries {
|
|
metric, vals, err := ParseSeriesDesc(test.input)
|
|
|
|
// Unexpected errors are always caused by a bug.
|
|
require.NotEqual(t, err, errUnexpected, "unexpected error occurred")
|
|
|
|
if !test.fail {
|
|
require.NoError(t, err)
|
|
testutil.RequireEqual(t, test.expectedMetric, metric, "error on input '%s'", test.input)
|
|
require.Equal(t, test.expectedValues, vals, "error in input '%s'", test.input)
|
|
} else {
|
|
require.Error(t, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRecoverParserRuntime(t *testing.T) {
|
|
p := NewParser("foo bar")
|
|
var err error
|
|
|
|
defer func() {
|
|
require.Equal(t, errUnexpected, err)
|
|
}()
|
|
defer p.recover(&err)
|
|
// Cause a runtime panic.
|
|
var a []int
|
|
a[123] = 1
|
|
}
|
|
|
|
func TestRecoverParserError(t *testing.T) {
|
|
p := NewParser("foo bar")
|
|
var err error
|
|
|
|
e := errors.New("custom error")
|
|
|
|
defer func() {
|
|
require.EqualError(t, err, e.Error())
|
|
}()
|
|
defer p.recover(&err)
|
|
|
|
panic(e)
|
|
}
|
|
|
|
func TestExtractSelectors(t *testing.T) {
|
|
for _, tc := range [...]struct {
|
|
input string
|
|
expected []string
|
|
}{
|
|
{
|
|
"foo",
|
|
[]string{`{__name__="foo"}`},
|
|
}, {
|
|
`foo{bar="baz"}`,
|
|
[]string{`{bar="baz", __name__="foo"}`},
|
|
}, {
|
|
`foo{bar="baz"} / flip{flop="flap"}`,
|
|
[]string{`{bar="baz", __name__="foo"}`, `{flop="flap", __name__="flip"}`},
|
|
}, {
|
|
`rate(foo[5m])`,
|
|
[]string{`{__name__="foo"}`},
|
|
}, {
|
|
`vector(1)`,
|
|
[]string{},
|
|
},
|
|
} {
|
|
expr, err := ParseExpr(tc.input)
|
|
require.NoError(t, err)
|
|
|
|
var expected [][]*labels.Matcher
|
|
for _, s := range tc.expected {
|
|
selector, err := ParseMetricSelector(s)
|
|
require.NoError(t, err)
|
|
expected = append(expected, selector)
|
|
}
|
|
|
|
require.Equal(t, expected, ExtractSelectors(expr))
|
|
}
|
|
}
|
|
|
|
func TestParseCustomFunctions(t *testing.T) {
|
|
funcs := Functions
|
|
funcs["custom_func"] = &Function{
|
|
Name: "custom_func",
|
|
ArgTypes: []ValueType{ValueTypeMatrix},
|
|
ReturnType: ValueTypeVector,
|
|
}
|
|
input := "custom_func(metric[1m])"
|
|
p := NewParser(input, WithFunctions(funcs))
|
|
expr, err := p.ParseExpr()
|
|
require.NoError(t, err)
|
|
|
|
call, ok := expr.(*Call)
|
|
require.True(t, ok)
|
|
require.Equal(t, "custom_func", call.Func.Name)
|
|
}
|