// 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" "math" "testing" "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: ` +{"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: 17, // }, // }, // StartPos: 1, // }, // }, // { // input: "", // fail: true, // errMsg: "no expression found in input", // }, // { // input: "# just a comment\n\n", // fail: true, // errMsg: "no expression found in input", // }, // { // input: "1+", // fail: true, // errMsg: "unexpected end of input", // }, // { // input: ".", // fail: true, // errMsg: "unexpected character: '.'", // }, // { // input: "2.5.", // fail: true, // errMsg: "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 ", // }, // { // 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 ", // }, // { // 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 ", // }, // // 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 ", // }, // { // input: "foo and on(bar) group_left(baz) bar", // fail: true, // errMsg: "no grouping allowed for \"and\" operation", // }, // { // input: "foo and on(bar) group_right(baz) bar", // fail: true, // errMsg: "no grouping allowed for \"and\" operation", // }, // { // input: "foo or on(bar) group_left(baz) bar", // fail: true, // errMsg: "no grouping allowed for \"or\" operation", // }, // { // input: "foo or on(bar) group_right(baz) bar", // fail: true, // errMsg: "no grouping allowed for \"or\" operation", // }, // { // input: "foo unless on(bar) group_left(baz) bar", // fail: true, // errMsg: "no grouping allowed for \"unless\" operation", // }, // { // input: "foo unless on(bar) group_right(baz) bar", // fail: true, // errMsg: "no grouping allowed for \"unless\" operation", // }, // { // input: `http_requests{group="production"} + on(instance) group_left(job,instance) cpu_count{type="smp"}`, // fail: true, // errMsg: "label \"instance\" must not occur in ON and GROUP clause at once", // }, // { // input: "foo + bool bar", // fail: true, // errMsg: "bool modifier can only be used on comparison operators", // }, // { // input: "foo + bool 10", // fail: true, // errMsg: "bool modifier can only be used on comparison operators", // }, // { // input: "foo and bool 10", // fail: true, // errMsg: "bool modifier can only be used on comparison operators", // }, // // Test Vector selector. // { // input: "foo", // expected: &VectorSelector{ // Name: "foo", // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 3, // }, // }, // }, // { // input: "min", // expected: &VectorSelector{ // Name: "min", // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "min"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 3, // }, // }, // }, // { // input: "foo offset 5m", // expected: &VectorSelector{ // Name: "foo", // OriginalOffset: 5 * time.Minute, // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 13, // }, // }, // }, // { // input: "foo offset -7m", // expected: &VectorSelector{ // Name: "foo", // OriginalOffset: -7 * time.Minute, // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 14, // }, // }, // }, // { // input: `foo OFFSET 1h30m`, // expected: &VectorSelector{ // Name: "foo", // OriginalOffset: 90 * time.Minute, // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 16, // }, // }, // }, // { // input: `foo OFFSET 1m30ms`, // expected: &VectorSelector{ // Name: "foo", // OriginalOffset: time.Minute + 30*time.Millisecond, // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 17, // }, // }, // }, // { // input: `foo @ 1603774568`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(1603774568000), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 16, // }, // }, // }, // { // input: `foo @ -100`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(-100000), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 10, // }, // }, // }, // { // input: `foo @ .3`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(300), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 8, // }, // }, // }, // { // input: `foo @ 3.`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(3000), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 8, // }, // }, // }, // { // input: `foo @ 3.33`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(3330), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 10, // }, // }, // }, // { // Rounding off. // input: `foo @ 3.3333`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(3333), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 12, // }, // }, // }, // { // Rounding off. // input: `foo @ 3.3335`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(3334), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 12, // }, // }, // }, // { // input: `foo @ 3e2`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(300000), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 9, // }, // }, // }, // { // input: `foo @ 3e-1`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(300), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 10, // }, // }, // }, // { // input: `foo @ 0xA`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(10000), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 9, // }, // }, // }, // { // input: `foo @ -3.3e1`, // expected: &VectorSelector{ // Name: "foo", // Timestamp: makeInt64Pointer(-33000), // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 12, // }, // }, // }, // { // input: `foo @ +Inf`, // fail: true, // errMsg: "1:1: parse error: timestamp out of bounds for @ modifier: +Inf", // }, // { // input: `foo @ -Inf`, // fail: true, // errMsg: "1:1: parse error: timestamp out of bounds for @ modifier: -Inf", // }, // { // input: `foo @ NaN`, // fail: true, // errMsg: "1:1: parse error: timestamp out of bounds for @ modifier: NaN", // }, // { // input: fmt.Sprintf(`foo @ %f`, float64(math.MaxInt64)+1), // fail: true, // errMsg: fmt.Sprintf("1:1: parse error: timestamp out of bounds for @ modifier: %f", float64(math.MaxInt64)+1), // }, // { // input: fmt.Sprintf(`foo @ %f`, float64(math.MinInt64)-1), // fail: true, // errMsg: fmt.Sprintf("1:1: parse error: timestamp out of bounds for @ modifier: %f", float64(math.MinInt64)-1), // }, // { // input: `foo:bar{a="bc"}`, // expected: &VectorSelector{ // Name: "foo:bar", // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, "a", "bc"), // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo:bar"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 15, // }, // }, // }, { input: `{"foo"}`, expected: &VectorSelector{ Name: "foo", LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), MustLabelMatcher(labels.MatchEqual, "a", "bc"), }, PosRange: posrange.PositionRange{ Start: 0, End: 6, }, }, }, // { // input: `{"foo", a="bc"}`, // expected: &VectorSelector{ // Name: "foo:bar", // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // MustLabelMatcher(labels.MatchEqual, "a", "bc"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 15, // }, // }, // }, // { // input: `foo{NaN='bc'}`, // expected: &VectorSelector{ // Name: "foo", // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, "NaN", "bc"), // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 13, // }, // }, // }, // { // input: `foo{bar='}'}`, // expected: &VectorSelector{ // Name: "foo", // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, "bar", "}"), // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 12, // }, // }, // }, // { // input: `foo{a="b", foo!="bar", test=~"test", bar!~"baz"}`, // expected: &VectorSelector{ // Name: "foo", // LabelMatchers: []*labels.Matcher{ // MustLabelMatcher(labels.MatchEqual, "a", "b"), // MustLabelMatcher(labels.MatchNotEqual, "foo", "bar"), // MustLabelMatcher(labels.MatchRegexp, "test", "test"), // MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"), // MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), // }, // PosRange: posrange.PositionRange{ // Start: 0, // End: 48, // }, // }, // }, // { // 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 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 ", // }, // { // input: `sum without (test) (some_metric) by (test)`, // fail: true, // errMsg: "unexpected ", // }, // { // 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 `, // }, // { // 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 } //{"foo", a="bc"} 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.LessOrEqual(t, 0, e.PositionRange.Start, "parse error has negative position\nExpression '%s'\nError: %v", test.input, e) require.LessOrEqual(t, e.PositionRange.Start, e.PositionRange.End, "parse error has negative length\nExpression '%s'\nError: %v", test.input, e) require.LessOrEqual(t, e.PositionRange.End, posrange.Pos(len(test.input)), "parse error is not contained in input\nExpression '%s'\nError: %v", test.input, e) } } }) } } // NaN has no equality. Thus, we need a separate test for it. func TestNaNExpression(t *testing.T) { expr, err := ParseExpr("NaN") require.NoError(t, err) nl, ok := expr.(*NumberLiteral) require.True(t, ok, "expected number literal but got %T", expr) require.True(t, math.IsNaN(nl.Val), "expected 'NaN' in number literal but got %v", nl.Val) } var testSeries = []struct { input string expectedMetric labels.Labels expectedValues []SequenceValue fail bool }{ { input: `{} 1 2 3`, expectedMetric: labels.EmptyLabels(), expectedValues: newSeq(1, 2, 3), }, { input: `{a="b"} -1 2 3`, expectedMetric: labels.FromStrings("a", "b"), expectedValues: newSeq(-1, 2, 3), }, { input: `my_metric 1 2 3`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric"), expectedValues: newSeq(1, 2, 3), }, { input: `my_metric{} 1 2 3`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric"), expectedValues: newSeq(1, 2, 3), }, { input: `my_metric{a="b"} 1 2 3`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, 2, 3), }, { input: `my_metric{a="b"} 1 2 3-10x4`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, 2, 3, -7, -17, -27, -37), }, { input: `my_metric{a="b"} 1 2 3-0x4`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, 2, 3, 3, 3, 3, 3), }, { input: `{} 1+1`, fail: true, }, { input: `{} 1x0`, expectedMetric: labels.EmptyLabels(), expectedValues: newSeq(1), }, { input: `{} 1+1x0`, expectedMetric: labels.EmptyLabels(), expectedValues: newSeq(1), }, { input: `my_metric{a="b"} 1 3 _ 5 _x4`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, 3, none, 5, none, none, none, none), }, { input: `my_metric{a="b"} 1 3 _ 5 _a4`, fail: true, }, { input: `my_metric{a="b"} 1 -1`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, -1), }, { input: `my_metric{a="b"} 1 +1`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, 1), }, { input: `my_metric{a="b"} 1 -1 -3-10x4 7 9 +5`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, -1, -3, -13, -23, -33, -43, 7, 9, 5), }, { input: `my_metric{a="b"} 1 +1 +4 -6 -2 8`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, 1, 4, -6, -2, 8), }, { // Trailing spaces should be correctly handles. input: `my_metric{a="b"} 1 2 3 `, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", "b"), expectedValues: newSeq(1, 2, 3), }, { // Handle escaped unicode characters as whole label values. input: `my_metric{a="\u70ac"} 1 2 3`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", `炬`), expectedValues: newSeq(1, 2, 3), }, { // Handle escaped unicode characters as partial label values. input: `my_metric{a="\u70ac = torch"} 1 2 3`, expectedMetric: labels.FromStrings(labels.MetricName, "my_metric", "a", `炬 = torch`), expectedValues: newSeq(1, 2, 3), }, { input: `my_metric{a="b"} -3-3 -3`, fail: true, }, { input: `my_metric{a="b"} -3 -3-3`, fail: true, }, { input: `my_metric{a="b"} -3 _-2`, fail: true, }, { input: `my_metric{a="b"} -3 3+3x4-4`, fail: true, }, } // For these tests only, we use the smallest float64 to signal an omitted value. const none = math.SmallestNonzeroFloat64 func newSeq(vals ...float64) (res []SequenceValue) { for _, v := range vals { if v == none { res = append(res, SequenceValue{Omitted: true}) } else { res = append(res, SequenceValue{Value: v}) } } return res } func TestParseHistogramSeries(t *testing.T) { for _, test := range []struct { name string input string expected []histogram.FloatHistogram expectedError string }{ { name: "empty histogram", input: "{} {{}}", expected: []histogram.FloatHistogram{{}}, }, { name: "empty histogram with space", input: "{} {{ }}", expected: []histogram.FloatHistogram{{}}, }, { name: "all properties used", input: `{} {{schema:1 sum:-0.3 count:3.1 z_bucket:7.1 z_bucket_w:0.05 buckets:[5.1 10 7] offset:-3 n_buckets:[4.1 5] n_offset:-5}}`, 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 "" "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 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) }