mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-26 22:19:40 -08:00
69edd8709b
Return annotations (warnings and infos) from PromQL queries This generalizes the warnings we have already used before (but only for problems with remote read) as "annotations". Annotations can be warnings or infos (the latter could be false positives). We do not treat them different in the API for now and return them all as "warnings". It would be easy to distinguish them and return infos separately, should that appear useful in the future. The new annotations are then used to create a lot of warnings or infos during PromQL evaluations. Partially these are things we have wanted for a long time (e.g. inform the user that they have applied `rate` to a metric that doesn't look like a counter), but the new native histograms have created even more needs for those annotations (e.g. if a query tries to aggregate float numbers with histograms). The annotations added here are not yet complete. A prominent example would be a warning about a range too short for a rate calculation. But such a warnings is more tricky to create with good fidelity and we will tackle it later. Another TODO is to take annotations into account when evaluating recording rules. --------- Signed-off-by: Jeanette Tan <jeanette.tan@grafana.com>
4109 lines
95 KiB
Go
4109 lines
95 KiB
Go
// 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/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: "",
|
|
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: "unexpected character: '.'",
|
|
},
|
|
{
|
|
input: "100..4",
|
|
fail: true,
|
|
errMsg: `unexpected number ".4"`,
|
|
},
|
|
{
|
|
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{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,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
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,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
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",
|
|
},
|
|
{
|
|
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 "}"`,
|
|
},
|
|
// 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"}[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: `foo[5mm]`,
|
|
fail: true,
|
|
errMsg: "bad duration syntax: \"5mm\"",
|
|
},
|
|
{
|
|
input: `foo[5m1]`,
|
|
fail: true,
|
|
errMsg: "bad 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: "missing unit character in duration",
|
|
},
|
|
{
|
|
input: `foo[1]`,
|
|
fail: true,
|
|
errMsg: "missing unit character in duration",
|
|
},
|
|
{
|
|
input: `some_metric[5m] OFFSET 1`,
|
|
fail: true,
|
|
errMsg: "unexpected number \"1\" in offset, expected duration",
|
|
},
|
|
{
|
|
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 duration",
|
|
},
|
|
{
|
|
input: `some_metric OFFSET 1m[5m]`,
|
|
fail: true,
|
|
errMsg: "1:22: parse error: no offset modifiers allowed before range",
|
|
},
|
|
{
|
|
input: `some_metric[5m] @ 1m`,
|
|
fail: true,
|
|
errMsg: "1:19: parse error: unexpected duration \"1m\" in @, expected timestamp",
|
|
},
|
|
{
|
|
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: "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"}[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 TestParseExpressions(t *testing.T) {
|
|
for _, test := range testExpr {
|
|
t.Run(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)
|
|
require.Equal(t, test.expected, expr, "error on input '%s'", test.input)
|
|
} else {
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), 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.True(t, 0 <= e.PositionRange.Start, "parse error has negative position\nExpression '%s'\nError: %v", test.input, e)
|
|
require.True(t, e.PositionRange.Start <= e.PositionRange.End, "parse error has negative length\nExpression '%s'\nError: %v", test.input, e)
|
|
require.True(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}}`,
|
|
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}},
|
|
}},
|
|
},
|
|
{
|
|
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 }}`,
|
|
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}},
|
|
}},
|
|
},
|
|
{
|
|
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`,
|
|
},
|
|
} {
|
|
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]}}`,
|
|
},
|
|
} {
|
|
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)
|
|
require.Equal(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
|
|
//nolint:govet
|
|
a[123] = 1
|
|
}
|
|
|
|
func TestRecoverParserError(t *testing.T) {
|
|
p := NewParser("foo bar")
|
|
var err error
|
|
|
|
e := errors.New("custom error")
|
|
|
|
defer func() {
|
|
require.Equal(t, e.Error(), err.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)
|
|
}
|