From 7e22d2e5c057c21d7e6de9ed0532c33239fd694a Mon Sep 17 00:00:00 2001 From: Lukasz Mierzwa Date: Wed, 13 Aug 2025 17:54:36 +0100 Subject: [PATCH] Refactor TestParseExpressions to be more explicit about errors Right now TestParseExpressions tests if a query returns an error but it only does a fuzzy check on returned errors. The error returned by the parser is ParseErrors, which is a slice of ParseErr structs. The Error() method on ParseErrors will return an error string based on the first error in that slice. This hides other returned errors so we can end up with bogus errors being returned but won't ever find this via this test. This change makes the test compare returned error (which is always ParseErrors type) with expected ParseErrors slice. The extra benefit of this is that current tests mostly ignore error positional range and only test for correct error message. Now errors must return expected positional information. There are a few cases uncovered where the positional informatio of errors seems wrong, added FIXME for these lines. Signed-off-by: Lukasz Mierzwa --- promql/parser/parse_test.go | 3221 +++++++++++++++++++---------------- 1 file changed, 1733 insertions(+), 1488 deletions(-) diff --git a/promql/parser/parse_test.go b/promql/parser/parse_test.go index 851c175e15..bf9a467076 100644 --- a/promql/parser/parse_test.go +++ b/promql/parser/parse_test.go @@ -21,6 +21,7 @@ import ( "testing" "time" + "github.com/google/go-cmp/cmp" "github.com/prometheus/common/model" "github.com/stretchr/testify/require" @@ -30,11 +31,25 @@ import ( "github.com/prometheus/prometheus/util/testutil" ) +func repeatError(query string, err error, start, startStep, end, endStep, count int) (errs ParseErrors) { + for i := 0; i < count; i++ { + errs = append(errs, ParseErr{ + PositionRange: posrange.PositionRange{ + Start: posrange.Pos(start + (i * startStep)), + End: posrange.Pos(end + (i * endStep)), + }, + Err: err, + Query: query, + }) + } + return errs +} + 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. + input string // The input to be parsed. + expected Expr // The expected expression AST. + fail bool // Whether parsing is supposed to fail. + errors ParseErrors // The errors that should be returned. }{ // Scalars and scalar-to-scalar operations. { @@ -433,10 +448,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 1, - End: 12, - }, + PosRange: posrange.PositionRange{Start: 1, End: 12}, }, }, }, @@ -449,10 +461,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 1, - End: 12, - }, + PosRange: posrange.PositionRange{Start: 1, End: 12}, }, }, }, @@ -465,10 +474,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 2, - End: 13, - }, + PosRange: posrange.PositionRange{Start: 2, End: 13}, }, StartPos: 1, }, @@ -481,138 +487,285 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 2, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 2, End: 17}, }, StartPos: 1, }, }, { - input: "", - fail: true, - errMsg: "no expression found in input", + input: "", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 0}, + Err: errors.New("no expression found in input"), + Query: "", + }, + }, }, { - input: "# just a comment\n\n", - fail: true, - errMsg: "no expression found in input", + input: "# just a comment\n\n", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 0}, + Err: errors.New("no expression found in input"), + Query: "# just a comment\n\n", + }, + }, }, { - input: "1+", - fail: true, - errMsg: "unexpected end of input", + input: "1+", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 2, End: 2}, + Err: errors.New("unexpected end of input"), + Query: "1+", + }, + }, }, { - input: ".", - fail: true, - errMsg: "unexpected character: '.'", + input: ".", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 1}, + Err: errors.New("unexpected character: '.'"), + Query: ".", + }, + }, }, { - input: "2.5.", - fail: true, - errMsg: `1:1: parse error: bad number or duration syntax: "2.5."`, + input: "2.5.", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 4}, + Err: errors.New(`bad number or duration syntax: "2.5."`), + Query: "2.5.", + }, + }, }, { - input: "100..4", - fail: true, - errMsg: `1:1: parse error: bad number or duration syntax: "100.."`, + input: "100..4", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 6}, + Err: errors.New(`bad number or duration syntax: "100.."`), + Query: "100..4", + }, + }, }, { - input: "0deadbeef", - fail: true, - errMsg: "bad number or duration syntax: \"0de\"", + input: "0deadbeef", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 9}, + Err: errors.New("bad number or duration syntax: \"0de\""), + Query: "0deadbeef", + }, + }, }, { - input: "1 /", - fail: true, - errMsg: "unexpected end of input", + input: "1 /", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 3, End: 3}, + Err: errors.New("unexpected end of input"), + Query: "1 /", + }, + }, }, { - input: "*1", - fail: true, - errMsg: "unexpected ", + input: "*1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 1}, + Err: errors.New("unexpected "), + Query: "*1", + }, + }, }, { - input: "(1))", - fail: true, - errMsg: "unexpected right parenthesis ')'", + input: "(1))", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 4}, + Err: errors.New("unexpected right parenthesis ')'"), + Query: "(1))", + }, + }, }, { - input: "((1)", - fail: true, - errMsg: "unclosed left parenthesis", + input: "((1)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 4}, + Err: errors.New("unclosed left parenthesis"), + Query: "((1)", + }, + }, }, { - input: "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", - fail: true, - errMsg: "out of range", + input: "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 309}, + Err: errors.New(`error parsing number: strconv.ParseFloat: parsing "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999": value out of range`), + Query: "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", + }, + }, }, { - input: "(", - fail: true, - errMsg: "unclosed left parenthesis", + input: "(", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 1, End: 1}, + Err: errors.New("unclosed left parenthesis"), + Query: "(", + }, + }, }, { - input: "1 and 1", - fail: true, - errMsg: "set operator \"and\" not allowed in binary scalar expression", + input: "1 and 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 7}, + Err: errors.New("set operator \"and\" not allowed in binary scalar expression"), + Query: "1 and 1", + }, + }, }, { - input: "1 == 1", - fail: true, - errMsg: "1:3: parse error: comparisons between scalars must use BOOL modifier", + input: "1 == 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 2, End: 3}, + Err: errors.New("comparisons between scalars must use BOOL modifier"), + Query: "1 == 1", + }, + }, }, { - input: "1 or 1", - fail: true, - errMsg: "set operator \"or\" not allowed in binary scalar expression", + input: "1 or 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 6}, + Err: errors.New("set operator \"or\" not allowed in binary scalar expression"), + Query: "1 or 1", + }, + }, }, { - input: "1 unless 1", - fail: true, - errMsg: "set operator \"unless\" not allowed in binary scalar expression", + input: "1 unless 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 10}, + Err: errors.New("set operator \"unless\" not allowed in binary scalar expression"), + Query: "1 unless 1", + }, + }, }, { - input: "1 !~ 1", - fail: true, - errMsg: `unexpected character after '!': '~'`, + input: "1 !~ 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 2, End: 6}, + Err: errors.New("unexpected character after '!': '~'"), + Query: "1 !~ 1", + }, + }, }, { - input: "1 =~ 1", - fail: true, - errMsg: `unexpected character after '=': '~'`, + input: "1 =~ 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 2, End: 6}, + Err: errors.New("unexpected character after '=': '~'"), + Query: "1 =~ 1", + }, + }, }, { - input: `-"string"`, - fail: true, - errMsg: `unary expression only allowed on expressions of type scalar or instant vector, got "string"`, + input: `-"string"`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 9}, + Err: errors.New(`unary expression only allowed on expressions of type scalar or instant vector, got "string"`), + Query: `-"string"`, + }, + }, }, { - input: `-test[5m]`, - fail: true, - errMsg: `unary expression only allowed on expressions of type scalar or instant vector, got "range vector"`, + input: `-test[5m]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 9}, + Err: errors.New(`unary expression only allowed on expressions of type scalar or instant vector, got "range vector"`), + Query: `-test[5m]`, + }, + }, }, { - input: `*test`, - fail: true, - errMsg: "unexpected ", + input: `*test`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 1}, + Err: errors.New("unexpected "), + Query: `*test`, + }, + }, }, { - 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: "1 offset 1d", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 1}, + Err: errors.New("offset modifier must be preceded by an instant vector selector or range vector selector or a subquery"), + Query: "1 offset 1d", + }, + }, }, { - input: "foo offset 1s offset 2s", - fail: true, - errMsg: "offset may not be set multiple times", + input: "foo offset 1s offset 2s", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 13}, + Err: errors.New("offset may not be set multiple times"), + Query: "foo offset 1s offset 2s", + }, + }, }, { - input: "a - on(b) ignoring(c) d", - fail: true, - errMsg: "1:11: parse error: unexpected ", + input: "a - on(b) ignoring(c) d", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 10, End: 18}, + Err: errors.New("unexpected "), + Query: "a - on(b) ignoring(c) d", + }, + }, }, // Vector selectors. { @@ -624,10 +777,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "step", "1s"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "offset"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 0, End: 17}, }, Range: 5 * time.Minute, EndPos: 21, @@ -642,10 +792,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "offset", "1s"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "step"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 0, End: 17}, }, Range: 5 * time.Minute, EndPos: 21, @@ -661,20 +808,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 6, End: 9}, }, VectorMatching: &VectorMatching{Card: CardOneToOne}, }, @@ -688,20 +829,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 6, End: 9}, }, VectorMatching: &VectorMatching{Card: CardOneToOne}, }, @@ -715,10 +850,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RHS: &NumberLiteral{ Val: 1, @@ -735,10 +867,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RHS: &NumberLiteral{ Val: 1, @@ -760,10 +889,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: posrange.PositionRange{ - Start: 6, - End: 9, - }, + PosRange: posrange.PositionRange{Start: 6, End: 9}, }, }, }, @@ -776,20 +902,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 8, End: 11}, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, @@ -803,20 +923,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 7, End: 10}, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, @@ -830,20 +944,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 11, End: 14}, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, @@ -860,20 +968,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 6, End: 9}, }, VectorMatching: &VectorMatching{Card: CardOneToOne}, }, @@ -884,20 +986,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bla"), }, - PosRange: posrange.PositionRange{ - Start: 13, - End: 16, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 21, End: 25}, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, @@ -918,20 +1014,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 8, End: 11}, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, @@ -940,10 +1030,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"), }, - PosRange: posrange.PositionRange{ - Start: 19, - End: 22, - }, + PosRange: posrange.PositionRange{Start: 19, End: 22}, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, @@ -952,10 +1039,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "qux"), }, - PosRange: posrange.PositionRange{ - Start: 26, - End: 29, - }, + PosRange: posrange.PositionRange{Start: 26, End: 29}, }, VectorMatching: &VectorMatching{Card: CardManyToMany}, }, @@ -970,10 +1054,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RHS: &BinaryExpr{ Op: DIV, @@ -982,20 +1063,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bla"), }, - PosRange: posrange.PositionRange{ - Start: 14, - End: 17, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 51, End: 55}, }, VectorMatching: &VectorMatching{ Card: CardOneToMany, @@ -1020,20 +1095,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 20, End: 23}, }, VectorMatching: &VectorMatching{ Card: CardOneToOne, @@ -1051,20 +1120,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 31, End: 34}, }, VectorMatching: &VectorMatching{ Card: CardManyToOne, @@ -1082,20 +1145,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 22, End: 25}, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, @@ -1113,20 +1170,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 13, End: 16}, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, @@ -1144,20 +1195,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 28, End: 31}, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, @@ -1174,20 +1219,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 19, End: 22}, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, @@ -1204,20 +1243,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 19, End: 22}, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, @@ -1235,20 +1268,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 36, End: 39}, }, VectorMatching: &VectorMatching{ Card: CardManyToOne, @@ -1267,20 +1294,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 43, End: 46}, }, VectorMatching: &VectorMatching{ Card: CardManyToOne, @@ -1298,20 +1319,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 42, End: 45}, }, VectorMatching: &VectorMatching{ Card: CardManyToOne, @@ -1329,20 +1344,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 41, End: 44}, }, VectorMatching: &VectorMatching{ Card: CardOneToMany, @@ -1361,20 +1370,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 47, End: 50}, }, VectorMatching: &VectorMatching{ Card: CardOneToMany, @@ -1384,99 +1387,253 @@ var testExpr = []struct { }, }, { - input: "foo and 1", - fail: true, - errMsg: "set operator \"and\" not allowed in binary scalar expression", + input: "foo and 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 9}, + Err: errors.New("set operator \"and\" not allowed in binary scalar expression"), + Query: "foo and 1", + }, + }, }, { - input: "1 and foo", - fail: true, - errMsg: "set operator \"and\" not allowed in binary scalar expression", + input: "1 and foo", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 9}, + Err: errors.New("set operator \"and\" not allowed in binary scalar expression"), + Query: "1 and foo", + }, + }, }, { - input: "foo or 1", - fail: true, - errMsg: "set operator \"or\" not allowed in binary scalar expression", + input: "foo or 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 8}, + Err: errors.New("set operator \"or\" not allowed in binary scalar expression"), + Query: "foo or 1", + }, + }, }, { - input: "1 or foo", - fail: true, - errMsg: "set operator \"or\" not allowed in binary scalar expression", + input: "1 or foo", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 8}, + Err: errors.New("set operator \"or\" not allowed in binary scalar expression"), + Query: "1 or foo", + }, + }, }, { - input: "foo unless 1", - fail: true, - errMsg: "set operator \"unless\" not allowed in binary scalar expression", + input: "foo unless 1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 12}, + Err: errors.New("set operator \"unless\" not allowed in binary scalar expression"), + Query: "foo unless 1", + }, + }, }, { - input: "1 unless foo", - fail: true, - errMsg: "set operator \"unless\" not allowed in binary scalar expression", + input: "1 unless foo", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 12}, + Err: errors.New("set operator \"unless\" not allowed in binary scalar expression"), + Query: "1 unless foo", + }, + }, }, { - input: "1 or on(bar) foo", - fail: true, - errMsg: "vector matching only allowed between instant vectors", + input: "1 or on(bar) foo", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 16}, + Err: errors.New("vector matching only allowed between instant vectors"), + Query: "1 or on(bar) foo", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 16}, + Err: errors.New(`set operator "or" not allowed in binary scalar expression`), + Query: "1 or on(bar) foo", + }, + }, }, { - input: "foo == on(bar) 10", - fail: true, - errMsg: "vector matching only allowed between instant vectors", + input: "foo == on(bar) 10", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 17}, + Err: errors.New("vector matching only allowed between instant vectors"), + Query: "foo == on(bar) 10", + }, + }, }, { - input: "foo + group_left(baz) bar", - fail: true, - errMsg: "unexpected ", + input: "foo + group_left(baz) bar", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 6, End: 16}, + Err: errors.New("unexpected "), + Query: "foo + group_left(baz) bar", + }, + }, }, { - input: "foo and on(bar) group_left(baz) bar", - fail: true, - errMsg: "no grouping allowed for \"and\" operation", + input: "foo and on(bar) group_left(baz) bar", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 35}, + Err: errors.New("no grouping allowed for \"and\" operation"), + Query: "foo and on(bar) group_left(baz) bar", + }, + { + PositionRange: posrange.PositionRange{Start: 0, End: 35}, + Err: errors.New("set operations must always be many-to-many"), + Query: "foo and on(bar) group_left(baz) bar", + }, + }, }, { - input: "foo and on(bar) group_right(baz) bar", - fail: true, - errMsg: "no grouping allowed for \"and\" operation", + input: "foo and on(bar) group_right(baz) bar", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 36}, + Err: errors.New("no grouping allowed for \"and\" operation"), + Query: "foo and on(bar) group_right(baz) bar", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 36}, + Err: errors.New("set operations must always be many-to-many"), + Query: "foo and on(bar) group_right(baz) bar", + }, + }, }, { - input: "foo or on(bar) group_left(baz) bar", - fail: true, - errMsg: "no grouping allowed for \"or\" operation", + input: "foo or on(bar) group_left(baz) bar", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 34}, + Err: errors.New("no grouping allowed for \"or\" operation"), + Query: "foo or on(bar) group_left(baz) bar", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 34}, + Err: errors.New("set operations must always be many-to-many"), + Query: "foo or on(bar) group_left(baz) bar", + }, + }, }, { - input: "foo or on(bar) group_right(baz) bar", - fail: true, - errMsg: "no grouping allowed for \"or\" operation", + input: "foo or on(bar) group_right(baz) bar", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 35}, + Err: errors.New("no grouping allowed for \"or\" operation"), + Query: "foo or on(bar) group_right(baz) bar", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 35}, + Err: errors.New("set operations must always be many-to-many"), + Query: "foo or on(bar) group_right(baz) bar", + }, + }, }, { - input: "foo unless on(bar) group_left(baz) bar", - fail: true, - errMsg: "no grouping allowed for \"unless\" operation", + input: "foo unless on(bar) group_left(baz) bar", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 38}, + Err: errors.New("no grouping allowed for \"unless\" operation"), + Query: "foo unless on(bar) group_left(baz) bar", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 38}, + Err: errors.New("set operations must always be many-to-many"), + Query: "foo unless on(bar) group_left(baz) bar", + }, + }, }, { - input: "foo unless on(bar) group_right(baz) bar", - fail: true, - errMsg: "no grouping allowed for \"unless\" operation", + input: "foo unless on(bar) group_right(baz) bar", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 39}, + Err: errors.New("no grouping allowed for \"unless\" operation"), + Query: "foo unless on(bar) group_right(baz) bar", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 39}, + Err: errors.New("set operations must always be many-to-many"), + Query: "foo unless on(bar) group_right(baz) bar", + }, + }, }, { - 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: `http_requests{group="production"} + on(instance) group_left(job,instance) cpu_count{type="smp"}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 34, End: 72}, + Err: errors.New("label \"instance\" must not occur in ON and GROUP clause at once"), + Query: `http_requests{group="production"} + on(instance) group_left(job,instance) cpu_count{type="smp"}`, + }, + }, }, { - input: "foo + bool bar", - fail: true, - errMsg: "bool modifier can only be used on comparison operators", + input: "foo + bool bar", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 9}, + Err: errors.New("bool modifier can only be used on comparison operators"), + Query: "foo + bool bar", + }, + }, }, { - input: "foo + bool 10", - fail: true, - errMsg: "bool modifier can only be used on comparison operators", + input: "foo + bool 10", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 9}, + Err: errors.New("bool modifier can only be used on comparison operators"), + Query: "foo + bool 10", + }, + }, }, { - input: "foo and bool 10", - fail: true, - errMsg: "bool modifier can only be used on comparison operators", + input: "foo and bool 10", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 11}, + Err: errors.New("bool modifier can only be used on comparison operators"), + Query: "foo and bool 10", + }, + ParseErr{ + PositionRange: posrange.PositionRange{End: 15}, + Err: errors.New(`set operator "and" not allowed in binary scalar expression`), + Query: "foo and bool 10", + }, + }, }, // Test Vector selector. { @@ -1486,10 +1643,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, }, { @@ -1499,10 +1653,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "min"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, }, { @@ -1513,10 +1664,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 13, - }, + PosRange: posrange.PositionRange{Start: 0, End: 13}, }, }, { @@ -1527,9 +1675,17 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 14, + PosRange: posrange.PositionRange{Start: 0, End: 14}, + }, + }, + { + input: `http_requests{group="production"} + on(instance) group_left(job,instance) cpu_count{type="smp"}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 34, End: 72}, + Err: errors.New("label \"instance\" must not occur in ON and GROUP clause at once"), + Query: `http_requests{group="production"} + on(instance) group_left(job,instance) cpu_count{type="smp"}`, }, }, }, @@ -1541,10 +1697,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 16, - }, + PosRange: posrange.PositionRange{Start: 0, End: 16}, }, }, { @@ -1555,10 +1708,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 0, End: 17}, }, }, { @@ -1569,10 +1719,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 16, - }, + PosRange: posrange.PositionRange{Start: 0, End: 16}, }, }, { @@ -1583,10 +1730,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 10, - }, + PosRange: posrange.PositionRange{Start: 0, End: 10}, }, }, { @@ -1597,10 +1741,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 8, - }, + PosRange: posrange.PositionRange{Start: 0, End: 8}, }, }, { @@ -1611,10 +1752,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 8, - }, + PosRange: posrange.PositionRange{Start: 0, End: 8}, }, }, { @@ -1625,10 +1763,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 10, - }, + PosRange: posrange.PositionRange{Start: 0, End: 10}, }, }, { // Rounding off. @@ -1639,10 +1774,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 12, - }, + PosRange: posrange.PositionRange{Start: 0, End: 12}, }, }, { // Rounding off. @@ -1653,10 +1785,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 12, - }, + PosRange: posrange.PositionRange{Start: 0, End: 12}, }, }, { @@ -1667,10 +1796,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 9, - }, + PosRange: posrange.PositionRange{Start: 0, End: 9}, }, }, { @@ -1681,10 +1807,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 10, - }, + PosRange: posrange.PositionRange{Start: 0, End: 10}, }, }, { @@ -1695,10 +1818,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 9, - }, + PosRange: posrange.PositionRange{Start: 0, End: 9}, }, }, { @@ -1709,36 +1829,63 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 12, + PosRange: posrange.PositionRange{Start: 0, End: 12}, + }, + }, + { + input: `foo @ +Inf`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 3}, + Err: errors.New("timestamp out of bounds for @ modifier: +Inf"), + Query: `foo @ +Inf`, }, }, }, { - input: `foo @ +Inf`, - fail: true, - errMsg: "1:1: parse error: timestamp out of bounds for @ modifier: +Inf", + input: `foo @ -Inf`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 3}, + Err: errors.New("timestamp out of bounds for @ modifier: -Inf"), + Query: `foo @ -Inf`, + }, + }, }, { - input: `foo @ -Inf`, - fail: true, - errMsg: "1:1: parse error: timestamp out of bounds for @ modifier: -Inf", + input: `foo @ NaN`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 3}, + Err: errors.New("timestamp out of bounds for @ modifier: NaN"), + Query: `foo @ NaN`, + }, + }, }, { - 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, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 3}, + Err: fmt.Errorf("timestamp out of bounds for @ modifier: %f", float64(math.MaxInt64)+1), + Query: fmt.Sprintf(`foo @ %f`, float64(math.MaxInt64)+1), + }, + }, }, { - 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: fmt.Sprintf(`foo @ %f`, float64(math.MinInt64)-1), + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 3}, + Err: fmt.Errorf("timestamp out of bounds for @ modifier: %f", float64(math.MinInt64)-1), + Query: fmt.Sprintf(`foo @ %f`, float64(math.MinInt64)-1), + }, + }, }, { input: `foo:bar{a="bc"}`, @@ -1748,10 +1895,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "a", "bc"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo:bar"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 15, - }, + PosRange: posrange.PositionRange{Start: 0, End: 15}, }, }, { @@ -1761,10 +1905,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 7, - }, + PosRange: posrange.PositionRange{Start: 0, End: 7}, }, }, { @@ -1775,10 +1916,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, `foo'bar`), MustLabelMatcher(labels.MatchEqual, `a\dos\path`, `boo\urns`), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 40, - }, + PosRange: posrange.PositionRange{Start: 0, End: 40}, }, }, { @@ -1789,10 +1927,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, `foo'bar`), MustLabelMatcher(labels.MatchEqual, `a\dos\path`, "boo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 32, - }, + PosRange: posrange.PositionRange{Start: 0, End: 32}, }, }, { @@ -1803,10 +1938,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), MustLabelMatcher(labels.MatchEqual, "a", "bc"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 15, - }, + PosRange: posrange.PositionRange{Start: 0, End: 15}, }, }, { @@ -1817,10 +1949,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "NaN", "bc"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 13, - }, + PosRange: posrange.PositionRange{Start: 0, End: 13}, }, }, { @@ -1831,10 +1960,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "}"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 12, - }, + PosRange: posrange.PositionRange{Start: 0, End: 12}, }, }, { @@ -1848,10 +1974,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 48, - }, + PosRange: posrange.PositionRange{Start: 0, End: 48}, }, }, { @@ -1865,10 +1988,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchRegexp, "test", "test"), MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 52, - }, + PosRange: posrange.PositionRange{Start: 0, End: 52}, }, }, { @@ -1882,10 +2002,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchNotRegexp, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 49, - }, + PosRange: posrange.PositionRange{Start: 0, End: 49}, }, }, { @@ -1896,10 +2013,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchRegexp, model.MetricNameLabel, "bar"), MustLabelMatcher(labels.MatchNotRegexp, model.MetricNameLabel, "baz"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 34, - }, + PosRange: posrange.PositionRange{Start: 0, End: 34}, }, }, { @@ -1910,10 +2024,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 32, - }, + PosRange: posrange.PositionRange{Start: 0, End: 32}, }, }, { @@ -1924,125 +2035,254 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "baz"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 23, + PosRange: posrange.PositionRange{Start: 0, End: 23}, + }, + }, + { + input: `{`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 1, End: 1}, + Err: errors.New("unexpected end of input inside braces"), + Query: `{`, }, }, }, { - input: `{`, - fail: true, - errMsg: "unexpected end of input inside braces", + input: `}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 1}, + Err: errors.New("unexpected character: '}'"), + Query: `}`, + }, + }, }, { - input: `}`, - fail: true, - errMsg: "unexpected character: '}'", + input: `some{`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 5, End: 5}, + Err: errors.New("unexpected end of input inside braces"), + Query: `some{`, + }, + }, }, { - input: `some{`, - fail: true, - errMsg: "unexpected end of input inside braces", + input: `some}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 5}, + Err: errors.New("unexpected character: '}'"), + Query: `some}`, + }, + }, }, { - input: `some}`, - fail: true, - errMsg: "unexpected character: '}'", + input: `some_metric{a=b}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 14, End: 15}, + Err: errors.New("unexpected identifier \"b\" in label matching, expected string"), + Query: `some_metric{a=b}`, + }, + }, }, { - input: `some_metric{a=b}`, - fail: true, - errMsg: "unexpected identifier \"b\" in label matching, expected string", + input: `some_metric{a:b="b"}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 13, End: 20}, + Err: errors.New("unexpected character inside braces: ':'"), + Query: `some_metric{a:b="b"}`, + }, + }, }, { - 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, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 5, End: 10}, + Err: errors.New("unexpected character inside braces: '*'"), + Query: `foo{a*"b"}`, + }, + }, }, { 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: '>'", + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 5, End: 11}, + Err: errors.New("unexpected character inside braces: '>'"), + Query: `foo{a>="b"}`, + }, + }, }, { - input: "some_metric{a=\"\xff\"}", - fail: true, - errMsg: "1:15: parse error: invalid UTF-8 rune", + input: "some_metric{a=\"\xff\"}", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 14, End: 18}, + Err: errors.New("invalid UTF-8 rune"), + Query: "some_metric{a=\"\xff\"}", + }, + }, }, { - input: `foo{gibberish}`, - fail: true, - errMsg: `unexpected "}" in label matching, expected label matching operator`, + input: `foo{gibberish}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 13, End: 14}, + Err: errors.New(`unexpected "}" in label matching, expected label matching operator`), + Query: `foo{gibberish}`, + }, + }, }, { - input: `foo{1}`, - fail: true, - errMsg: "unexpected character inside braces: '1'", + input: `foo{1}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 6}, + Err: errors.New("unexpected character inside braces: '1'"), + Query: `foo{1}`, + }, + }, }, { - input: `{}`, - fail: true, - errMsg: "vector selector must contain at least one non-empty matcher", + input: `{}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 2}, + Err: errors.New("vector selector must contain at least one non-empty matcher"), + Query: `{}`, + }, + }, }, { - input: `{x=""}`, - fail: true, - errMsg: "vector selector must contain at least one non-empty matcher", + input: `{x=""}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 6}, + Err: errors.New("vector selector must contain at least one non-empty matcher"), + Query: `{x=""}`, + }, + }, }, { - input: `{x=~".*"}`, - fail: true, - errMsg: "vector selector must contain at least one non-empty matcher", + input: `{x=~".*"}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 9}, + Err: errors.New("vector selector must contain at least one non-empty matcher"), + Query: `{x=~".*"}`, + }, + }, }, { - input: `{x!~".+"}`, - fail: true, - errMsg: "vector selector must contain at least one non-empty matcher", + input: `{x!~".+"}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 9}, + Err: errors.New("vector selector must contain at least one non-empty matcher"), + Query: `{x!~".+"}`, + }, + }, }, { - input: `{x!="a"}`, - fail: true, - errMsg: "vector selector must contain at least one non-empty matcher", + input: `{x!="a"}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 8}, + Err: errors.New("vector selector must contain at least one non-empty matcher"), + Query: `{x!="a"}`, + }, + }, }, // Although {"bar", __name__="baz"} is allowed (see above), specifying a // metric name inside and outside the braces is not. { - input: `foo{__name__="bar"}`, - fail: true, - errMsg: `metric name must not be set twice: "foo" or "bar"`, + input: `foo{__name__="bar"}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 19}, + Err: errors.New(`metric name must not be set twice: "foo" or "bar"`), + Query: `foo{__name__="bar"}`, + }, + }, }, { - input: `foo{__name__= =}`, - fail: true, - errMsg: `1:15: parse error: unexpected "=" in label matching, expected string`, + input: `foo{__name__= =}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 14, End: 15}, + Err: errors.New("unexpected \"=\" in label matching, expected string"), + Query: "foo{__name__= =}", + }, + }, }, { - input: `foo{,}`, - fail: true, - errMsg: `unexpected "," in label matching, expected identifier or "}"`, + input: `foo{,}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 5}, + Err: errors.New("unexpected \",\" in label matching, expected identifier or \"}\""), + Query: "foo{,}", + }, + }, }, { - input: `foo{__name__ == "bar"}`, - fail: true, - errMsg: `1:15: parse error: unexpected "=" in label matching, expected string`, + input: `foo{__name__ == "bar"}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 14, End: 15}, + Err: errors.New("unexpected \"=\" in label matching, expected string"), + Query: "foo{__name__ == \"bar\"}", + }, + }, }, { - input: `foo{__name__="bar" lol}`, - fail: true, - errMsg: `unexpected identifier "lol" in label matching, expected "," or "}"`, + input: `foo{__name__="bar" lol}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 19, End: 22}, + Err: errors.New(`unexpected identifier "lol" in label matching, expected "," or "}"`), + Query: `foo{__name__="bar" lol}`, + }, + }, }, { - input: `foo{"a"=}`, - fail: true, - errMsg: `unexpected "}" in label matching, expected string`, + input: `foo{"a"=}`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 8, End: 9}, + Err: errors.New(`unexpected "}" in label matching, expected string`), + Query: `foo{"a"=}`, + }, + }, }, // Test matrix selector. { @@ -2053,10 +2293,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 1000 * time.Millisecond, EndPos: 12, @@ -2070,10 +2307,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 1001 * time.Millisecond, EndPos: 12, @@ -2087,10 +2321,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 1002 * time.Millisecond, EndPos: 12, @@ -2104,10 +2335,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 5 * time.Second, EndPos: 8, @@ -2121,10 +2349,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 5 * time.Minute, EndPos: 8, @@ -2138,10 +2363,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 5*time.Minute + 30*time.Second, EndPos: 10, @@ -2156,10 +2378,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 5 * time.Hour, EndPos: 18, @@ -2174,10 +2393,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 5 * 24 * time.Hour, EndPos: 19, @@ -2192,10 +2408,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 5 * 7 * 24 * time.Hour, EndPos: 18, @@ -2211,10 +2424,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "a", "b"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 11, - }, + PosRange: posrange.PositionRange{Start: 0, End: 11}, }, Range: 5 * 365 * 24 * time.Hour, EndPos: 25, @@ -2230,10 +2440,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "a", "b"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 11, - }, + PosRange: posrange.PositionRange{Start: 0, End: 11}, }, Range: 5 * time.Minute, EndPos: 27, @@ -2248,10 +2455,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 3 * time.Millisecond, EndPos: 16, @@ -2266,10 +2470,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 4*time.Second + 180*time.Millisecond, EndPos: 20, @@ -2284,10 +2485,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 4*time.Second + 180*time.Millisecond, EndPos: 17, @@ -2302,10 +2500,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 4*time.Second + 18*time.Millisecond, EndPos: 19, @@ -2320,10 +2515,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 4*time.Second + 18*time.Millisecond, EndPos: 18, @@ -2339,10 +2531,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "a", "b"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 11, - }, + PosRange: posrange.PositionRange{Start: 0, End: 11}, }, Range: 5 * 365 * 24 * time.Hour, EndPos: 28, @@ -2356,10 +2545,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 5 * time.Second, EndPos: 7, @@ -2374,93 +2560,202 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 11, - }, + PosRange: posrange.PositionRange{Start: 0, End: 11}, }, Range: 5 * time.Minute, EndPos: 20, }, }, { - input: `foo[5mm]`, - fail: true, - errMsg: "bad number or duration syntax: \"5mm\"", + input: `foo[5mm]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 8}, + Err: errors.New("bad number or duration syntax: \"5mm\""), + Query: `foo[5mm]`, + }, + }, }, { - input: `foo[5m1]`, - fail: true, - errMsg: "bad number or duration syntax: \"5m1\"", + input: `foo[5m1]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 8}, + Err: errors.New("bad number or duration syntax: \"5m1\""), + Query: `foo[5m1]`, + }, + }, }, { - input: `foo[5m:1m1]`, - fail: true, - errMsg: "bad number or duration syntax: \"1m1\"", + input: `foo[5m:1m1]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 7, End: 11}, + Err: errors.New("bad number or duration syntax: \"1m1\""), + Query: `foo[5m:1m1]`, + }, + }, }, { - input: `foo[5y1hs]`, - fail: true, - errMsg: "unknown unit \"hs\" in duration \"5y1hs\"", + input: `foo[5y1hs]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 9}, + Err: errors.New("unknown unit \"hs\" in duration \"5y1hs\""), + Query: `foo[5y1hs]`, + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 9}, + Err: errors.New("duration must be greater than 0"), + Query: "foo[5y1hs]", + }, + }, }, { - input: `foo[5m1h]`, - fail: true, - errMsg: "not a valid duration string: \"5m1h\"", + input: `foo[5m1h]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 8}, + Err: errors.New("not a valid duration string: \"5m1h\""), + Query: `foo[5m1h]`, + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 8}, + Err: errors.New("duration must be greater than 0"), + Query: "foo[5m1h]", + }, + }, }, { - input: `foo[5m1m]`, - fail: true, - errMsg: "not a valid duration string: \"5m1m\"", + input: `foo[5m1m]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 8}, + Err: errors.New("not a valid duration string: \"5m1m\""), + Query: `foo[5m1m]`, + }, + { + PositionRange: posrange.PositionRange{Start: 4, End: 8}, + Err: errors.New(`duration must be greater than 0`), + Query: `foo[5m1m]`, + }, + }, }, { - input: `foo[0m]`, - fail: true, - errMsg: "duration must be greater than 0", + input: `foo[0m]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 6}, + Err: errors.New("duration must be greater than 0"), + Query: `foo[0m]`, + }, + }, }, { input: `foo["5m"]`, fail: true, + errors: ParseErrors{ + { + PositionRange: posrange.PositionRange{Start: 4, End: 9}, + Err: errors.New(`unexpected character in duration expression: '"'`), + Query: `foo["5m"]`, + }, + }, }, { - input: `foo[]`, - fail: true, - errMsg: "unexpected \"]\" in subquery or range selector, expected number, duration, or step()", + input: `foo[]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 5}, + Err: errors.New("unexpected \"]\" in subquery or range selector, expected number, duration, or step()"), + Query: `foo[]`, + }, + }, }, { - input: `foo[-1]`, - fail: true, - errMsg: "duration must be greater than 0", + input: `foo[-1]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 6}, + Err: errors.New("duration must be greater than 0"), + Query: `foo[-1]`, + }, + }, }, { - input: `some_metric[5m] OFFSET 1mm`, - fail: true, - errMsg: "bad number or duration syntax: \"1mm\"", + input: `some_metric[5m] OFFSET 1mm`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 23, End: 26}, + Err: errors.New("bad number or duration syntax: \"1mm\""), + Query: `some_metric[5m] OFFSET 1mm`, + }, + }, }, { - input: `some_metric[5m] OFFSET`, - fail: true, - errMsg: "1:23: parse error: unexpected end of input in offset, expected number, duration, or step()", + input: `some_metric[5m] OFFSET`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 22, End: 22}, + Err: errors.New("unexpected end of input in offset, expected number, duration, or step()"), + Query: `some_metric[5m] OFFSET`, + }, + }, }, { - input: `some_metric OFFSET 1m[5m]`, - fail: true, - errMsg: "1:22: parse error: no offset modifiers allowed before range", + input: `some_metric OFFSET 1m[5m]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 21, End: 25}, + Err: errors.New("no offset modifiers allowed before range"), + Query: `some_metric OFFSET 1m[5m]`, + }, + }, }, { - input: `some_metric[5m] @`, - fail: true, - errMsg: "1:18: parse error: unexpected end of input in @, expected timestamp", + input: `some_metric[5m] @`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 17, End: 17}, + Err: errors.New("unexpected end of input in @, expected timestamp"), + Query: `some_metric[5m] @`, + }, + }, }, { - input: `some_metric @ 1234 [5m]`, - fail: true, - errMsg: "1:20: parse error: no @ modifiers allowed before range", + input: `some_metric @ 1234 [5m]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 19, End: 23}, + Err: errors.New("no @ modifiers allowed before range"), + Query: `some_metric @ 1234 [5m]`, + }, + }, }, { - input: `(foo + bar)[5m]`, - fail: true, - errMsg: "1:12: parse error: ranges only allowed for vector selectors", + input: `(foo + bar)[5m]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 11, End: 15}, + Err: errors.New("ranges only allowed for vector selectors"), + Query: `(foo + bar)[5m]`, + }, + }, }, // Test aggregation. { @@ -2472,16 +2767,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 13, - End: 24, - }, + PosRange: posrange.PositionRange{Start: 13, End: 24}, }, Grouping: []string{"foo"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 25, - }, + PosRange: posrange.PositionRange{Start: 0, End: 25}, }, }, { @@ -2492,22 +2781,22 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some.metric"), }, - PosRange: posrange.PositionRange{ - Start: 19, - End: 34, - }, + PosRange: posrange.PositionRange{Start: 19, End: 34}, }, Grouping: []string{"foo bar"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 35, - }, + PosRange: posrange.PositionRange{Start: 0, End: 35}, }, }, { - input: `sum by ("foo)(some_metric{})`, - fail: true, - errMsg: "unterminated quoted string", + input: `sum by ("foo)(some_metric{})`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 8, End: 28}, + Err: errors.New("unterminated quoted string"), + Query: `sum by ("foo)(some_metric{})`, + }, + }, }, { input: `sum by ("foo", bar, 'baz')({"some.metric"})`, @@ -2517,16 +2806,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some.metric"), }, - PosRange: posrange.PositionRange{ - Start: 27, - End: 42, - }, + PosRange: posrange.PositionRange{Start: 27, End: 42}, }, Grouping: []string{"foo", "bar", "baz"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 43, - }, + PosRange: posrange.PositionRange{Start: 0, End: 43}, }, }, { @@ -2538,16 +2821,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 13, - End: 24, - }, + PosRange: posrange.PositionRange{Start: 13, End: 24}, }, Grouping: []string{"foo"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 25, - }, + PosRange: posrange.PositionRange{Start: 0, End: 25}, }, }, { @@ -2559,16 +2836,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 13, - End: 24, - }, + PosRange: posrange.PositionRange{Start: 13, End: 24}, }, Grouping: []string{"foo"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 25, - }, + PosRange: posrange.PositionRange{Start: 0, End: 25}, }, }, { @@ -2581,16 +2852,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 19, - End: 30, - }, + PosRange: posrange.PositionRange{Start: 19, End: 30}, }, Grouping: []string{"foo"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 31, - }, + PosRange: posrange.PositionRange{Start: 0, End: 31}, }, }, { @@ -2603,16 +2868,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 5, - End: 16, - }, + PosRange: posrange.PositionRange{Start: 5, End: 16}, }, Grouping: []string{"foo"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 31, - }, + PosRange: posrange.PositionRange{Start: 0, End: 31}, }, }, { @@ -2624,15 +2883,9 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 7, - End: 18, - }, - }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 19, + PosRange: posrange.PositionRange{Start: 7, End: 18}, }, + PosRange: posrange.PositionRange{Start: 0, End: 19}, }, }, { @@ -2644,16 +2897,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 16, - End: 27, - }, + PosRange: posrange.PositionRange{Start: 16, End: 27}, }, Grouping: []string{"foo"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 28, - }, + PosRange: posrange.PositionRange{Start: 0, End: 28}, }, }, { @@ -2665,16 +2912,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 10, - End: 21, - }, + PosRange: posrange.PositionRange{Start: 10, End: 21}, }, Grouping: []string{}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 22, - }, + PosRange: posrange.PositionRange{Start: 0, End: 22}, }, }, { @@ -2686,16 +2927,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 18, - End: 29, - }, + PosRange: posrange.PositionRange{Start: 18, End: 29}, }, Grouping: []string{"foo", "bar"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 30, - }, + PosRange: posrange.PositionRange{Start: 0, End: 30}, }, }, { @@ -2707,16 +2942,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 14, - End: 25, - }, + PosRange: posrange.PositionRange{Start: 14, End: 25}, }, Grouping: []string{"foo"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 26, - }, + PosRange: posrange.PositionRange{Start: 0, End: 26}, }, }, { @@ -2728,22 +2957,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 8, - End: 19, - }, + 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, + Val: 5, + PosRange: posrange.PositionRange{Start: 5, End: 6}, }, + PosRange: posrange.PositionRange{Start: 0, End: 20}, }, }, { @@ -2755,22 +2975,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 22, - End: 33, - }, + 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, + Val: "value", + PosRange: posrange.PositionRange{Start: 13, End: 20}, }, + PosRange: posrange.PositionRange{Start: 0, End: 34}, }, }, { @@ -2784,108 +2995,189 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 53, - End: 64, - }, + PosRange: posrange.PositionRange{Start: 53, End: 64}, }, Grouping: []string{"and", "by", "avg", "count", "alert", "annotations"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 65, + PosRange: posrange.PositionRange{Start: 0, End: 65}, + }, + }, + { + input: "sum without(==)(some_metric)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 12, End: 14}, + Err: errors.New("unexpected in grouping opts, expected label"), + Query: "sum without(==)(some_metric)", }, }, }, { - input: "sum without(==)(some_metric)", - fail: true, - errMsg: "unexpected in grouping opts, expected label", + input: "sum without(,)(some_metric)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 12, End: 13}, + Err: errors.New(`unexpected "," in grouping opts, expected label`), + Query: "sum without(,)(some_metric)", + }, + }, }, { - input: "sum without(,)(some_metric)", - fail: true, - errMsg: `unexpected "," in grouping opts, expected label`, + input: "sum without(foo,,)(some_metric)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 16, End: 17}, + Err: errors.New(`unexpected "," in grouping opts, expected label`), + Query: "sum without(foo,,)(some_metric)", + }, + }, }, { - input: "sum without(foo,,)(some_metric)", - fail: true, - errMsg: `unexpected "," in grouping opts, expected label`, + input: `sum some_metric by (test)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 15}, + Err: errors.New(`unexpected identifier "some_metric"`), + Query: `sum some_metric by (test)`, + }, + }, }, { - input: `sum some_metric by (test)`, - fail: true, - errMsg: "unexpected identifier \"some_metric\"", + input: `sum (some_metric) by test`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 21, End: 25}, + Err: errors.New(`unexpected identifier "test" in grouping opts, expected "("`), + Query: `sum (some_metric) by test`, + }, + }, }, { - input: `sum (some_metric) by test`, - fail: true, - errMsg: "unexpected identifier \"test\" in grouping opts", + input: `sum () by (test)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 16}, + Err: errors.New("no arguments for aggregate expression provided"), + Query: `sum () by (test)`, + }, + }, }, { - input: `sum (some_metric) by test`, - fail: true, - errMsg: "unexpected identifier \"test\" in grouping opts", + input: "MIN keep_common (some_metric)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 15}, + Err: errors.New("unexpected identifier \"keep_common\""), + Query: "MIN keep_common (some_metric)", + }, + }, }, { - input: `sum () by (test)`, - fail: true, - errMsg: "no arguments for aggregate expression provided", + input: "MIN (some_metric) keep_common", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 18, End: 29}, + Err: errors.New(`unexpected identifier "keep_common"`), + Query: "MIN (some_metric) keep_common", + }, + }, }, { - input: "MIN keep_common (some_metric)", - fail: true, - errMsg: "1:5: parse error: unexpected identifier \"keep_common\"", + input: `sum (some_metric) without (test) by (test)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 33, End: 35}, + Err: errors.New("unexpected "), + Query: `sum (some_metric) without (test) by (test)`, + }, + }, }, { - input: "MIN (some_metric) keep_common", - fail: true, - errMsg: `unexpected identifier "keep_common"`, + input: `sum without (test) (some_metric) by (test)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 33, End: 35}, + Err: errors.New("unexpected "), + Query: `sum without (test) (some_metric) by (test)`, + }, + }, }, { - input: `sum (some_metric) without (test) by (test)`, - fail: true, - errMsg: "unexpected ", + input: `topk(some_metric)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 17}, + Err: errors.New("wrong number of arguments for aggregate expression provided, expected 2, got 1"), + Query: `topk(some_metric)`, + }, + }, }, { - input: `sum without (test) (some_metric) by (test)`, - fail: true, - errMsg: "unexpected ", + input: `topk(some_metric,)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 16, End: 17}, + Err: errors.New("trailing commas not allowed in function call args"), + Query: `topk(some_metric,)`, + }, + { + PositionRange: posrange.PositionRange{Start: 0, End: 18}, + Err: errors.New("wrong number of arguments for aggregate expression provided, expected 2, got 1"), + Query: "topk(some_metric,)", + }, + }, }, { - input: `topk(some_metric)`, - fail: true, - errMsg: "wrong number of arguments for aggregate expression provided, expected 2, got 1", + input: `topk(some_metric, other_metric)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 5, End: 16}, + Err: errors.New("expected type scalar in aggregation parameter, got instant vector"), + Query: `topk(some_metric, other_metric)`, + }, + }, }, { - input: `topk(some_metric,)`, - fail: true, - errMsg: "trailing commas not allowed in function call args", + input: `count_values(5, other_metric)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 13, End: 14}, + Err: errors.New("expected type string in aggregation parameter, got scalar"), + Query: `count_values(5, other_metric)`, + }, + }, }, { - 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", + input: `rate(some_metric[5m]) @ 1234`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 21}, + Err: errors.New("@ modifier must be preceded by an instant vector selector or range vector selector or a subquery"), + Query: `rate(some_metric[5m]) @ 1234`, + }, + }, }, // Test function calls. { input: "time()", expected: &Call{ - Func: MustGetFunction("time"), - Args: Expressions{}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 6, - }, + Func: MustGetFunction("time"), + Args: Expressions{}, + PosRange: posrange.PositionRange{Start: 0, End: 6}, }, }, { @@ -2899,16 +3191,10 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchNotEqual, "foo", "bar"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 6, - End: 29, - }, + PosRange: posrange.PositionRange{Start: 6, End: 29}, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 30, - }, + PosRange: posrange.PositionRange{Start: 0, End: 30}, }, }, { @@ -2922,19 +3208,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 5, - End: 16, - }, + PosRange: posrange.PositionRange{Start: 5, End: 16}, }, Range: 5 * time.Minute, EndPos: 20, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 21, - }, + PosRange: posrange.PositionRange{Start: 0, End: 21}, }, }, { @@ -2947,16 +3227,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 6, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 6, End: 17}, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 18, - }, + PosRange: posrange.PositionRange{Start: 0, End: 18}, }, }, { @@ -2969,101 +3243,208 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 6, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 6, End: 17}, }, &NumberLiteral{ - Val: 5, - PosRange: posrange.PositionRange{ - Start: 19, - End: 20, - }, + Val: 5, + PosRange: posrange.PositionRange{Start: 19, End: 20}, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 21, + PosRange: posrange.PositionRange{Start: 0, End: 21}, + }, + }, + { + input: "floor()", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 7}, + Err: errors.New("expected 1 argument(s) in call to \"floor\", got 0"), + Query: "floor()", }, }, }, { - input: "floor()", - fail: true, - errMsg: "expected 1 argument(s) in call to \"floor\", got 0", + input: "floor(some_metric, other_metric)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 32}, + Err: errors.New("expected 1 argument(s) in call to \"floor\", got 2"), + Query: "floor(some_metric, other_metric)", + }, + }, }, { - 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, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 21}, + Err: errors.New("expected 1 argument(s) in call to \"floor\", got 2"), + Query: "floor(some_metric, 1)", + }, + }, }, { - input: "floor(some_metric, 1)", - fail: true, - errMsg: "expected 1 argument(s) in call to \"floor\", got 2", + input: "floor(1)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 6, End: 7}, + Err: errors.New("expected type instant vector in call to function \"floor\", got scalar"), + Query: "floor(1)", + }, + }, }, { - 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, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 43}, + Err: errors.New("expected at most 1 argument(s) in call to \"hour\", got 3"), + Query: "hour(some_metric, some_metric, some_metric)", + }, + }, }, { - 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, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 17}, + Err: errors.New("expected 0 argument(s) in call to \"time\", got 1"), + Query: "time(some_metric)", + }, + }, }, { - 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, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 29}, + Err: errors.New("unknown function with name \"non_existent_function_far_bar\""), + Query: "non_existent_function_far_bar()", + }, + }, }, { - input: "non_existent_function_far_bar()", - fail: true, - errMsg: "unknown function with name \"non_existent_function_far_bar\"", + input: "rate(some_metric)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 5, End: 16}, + Err: errors.New("expected type range vector in call to function \"rate\", got instant vector"), + Query: "rate(some_metric)", + }, + }, }, { - 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", + input: "label_replace(a, `b`, `c\xff`, `d`, `.*`)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 22, End: 38}, + Err: errors.New("invalid UTF-8 rune"), + Query: "label_replace(a, `b`, `c\xff`, `d`, `.*`)", + }, + { + PositionRange: posrange.PositionRange{Start: 20, End: 21}, + Err: errors.New("trailing commas not allowed in function call args"), + Query: "label_replace(a, `b`, `c\xff`, `d`, `.*`)", + }, + }, }, // Fuzzing regression tests. { - input: "-=", - fail: true, - errMsg: `unexpected "="`, + input: "*1", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 1}, + Err: errors.New("unexpected "), + Query: "*1", + }, + }, }, { - input: "++-++-+-+-<", - fail: true, - errMsg: `unexpected `, + input: "-=", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 1, End: 2}, + Err: errors.New(`unexpected "="`), + Query: "-=", + }, + }, }, { - input: "e-+=/(0)", - fail: true, - errMsg: `unexpected "="`, + input: "++-++-+-+-<", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 10, End: 11}, + Err: errors.New(`unexpected `), + Query: "++-++-+-+-<", + }, + }, }, { - input: "a>b()", - fail: true, - errMsg: `unknown function`, + input: "e-+=/(0)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 3, End: 4}, + Err: errors.New(`unexpected "="`), + Query: "e-+=/(0)", + }, + }, }, { - input: "rate(avg)", - fail: true, - errMsg: `expected type range vector`, + input: "a>b()", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 2, End: 3}, + Err: errors.New(`unknown function with name "b"`), + Query: "a>b()", + }, + }, + }, + { + input: "rate(avg)", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 5, End: 8}, + Err: errors.New(`expected type range vector in call to function "rate", got instant vector`), + Query: "rate(avg)", + }, + }, }, { // 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: "(" + strings.Repeat("-{}-1", 10000) + ")" + strings.Repeat("[1m:]", 1000), + fail: true, + // This test generates a lot of errors, so we need a helper function to generate it for us. + errors: append( + repeatError( + "("+strings.Repeat("-{}-1", 10000)+")"+strings.Repeat("[1m:]", 1000), + errors.New("vector selector must contain at least one non-empty matcher"), + 2, 5, // begin with start=2, increment by 5 each time + 4, 5, // begin with end=2, increment by 5 each time + 10000, // number of errors to generate + ), + repeatError( + "("+strings.Repeat("-{}-1", 10000)+")"+strings.Repeat("[1m:]", 1000), + errors.New("subquery is only allowed on instant vector, got matrix instead"), + 0, 0, // begin with start=0, don't increment, it's always start=0 + 50012, 5, // begin with end=50012, increment by 5 each time + 999, // number of errors to generate + )..., + ), }, { input: "sum(sum)", @@ -3074,15 +3455,9 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "sum"), }, - PosRange: posrange.PositionRange{ - Start: 4, - End: 7, - }, - }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 8, + PosRange: posrange.PositionRange{Start: 4, End: 7}, }, + PosRange: posrange.PositionRange{Start: 0, End: 8}, }, }, { @@ -3094,20 +3469,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "a"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 1, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 4, End: 7}, }, VectorMatching: &VectorMatching{}, }, @@ -3156,30 +3525,60 @@ var testExpr = []struct { }, }, { - input: "`\\``", - fail: true, - errMsg: "unterminated raw string", + input: "`\\``", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 3, End: 4}, + Err: errors.New("unterminated raw string"), + Query: "`\\``", + }, + }, }, { - input: `"\`, - fail: true, - errMsg: "escape sequence not terminated", + input: `"\`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 2}, + Err: errors.New("escape sequence not terminated"), + Query: `"\`, + }, + }, }, { - input: `"\c"`, - fail: true, - errMsg: "unknown escape sequence U+0063 'c'", + input: `"\c"`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 4}, + Err: errors.New("unknown escape sequence U+0063 'c'"), + Query: `"\c"`, + }, + }, }, { - input: `"\x."`, - fail: true, - errMsg: "illegal character U+002E '.' in escape sequence", + input: `"\x."`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 5}, + Err: errors.New("illegal character U+002E '.' in escape sequence"), + Query: `"\x."`, + }, + }, }, // Subquery. { - input: `foo{bar="baz"}[`, - fail: true, - errMsg: `unexpected end of input in duration expression`, + input: `foo{bar="baz"}[`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 15, End: 15}, + Err: errors.New(`unexpected end of input in duration expression`), + Query: `foo{bar="baz"}[`, + }, + }, }, { input: `foo{bar="baz"}[10m:6s]`, @@ -3190,10 +3589,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 14, - }, + PosRange: posrange.PositionRange{Start: 0, End: 14}, }, Range: 10 * time.Minute, Step: 6 * time.Second, @@ -3209,10 +3605,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 14, - }, + PosRange: posrange.PositionRange{Start: 0, End: 14}, }, Range: 10*time.Minute + 5*time.Second, Step: time.Hour + 6*time.Millisecond, @@ -3227,10 +3620,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 10 * time.Minute, EndPos: 9, @@ -3252,19 +3642,13 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 19, - End: 33, - }, + PosRange: posrange.PositionRange{Start: 19, End: 33}, }, Range: 2 * time.Second, EndPos: 37, }, }, - PosRange: posrange.PositionRange{ - Start: 14, - End: 38, - }, + PosRange: posrange.PositionRange{Start: 14, End: 38}, }, Range: 5 * time.Minute, Step: 5 * time.Second, @@ -3272,10 +3656,7 @@ var testExpr = []struct { EndPos: 45, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 46, - }, + PosRange: posrange.PositionRange{Start: 0, End: 46}, }, }, { @@ -3295,28 +3676,19 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 19, - End: 33, - }, + PosRange: posrange.PositionRange{Start: 19, End: 33}, }, Range: 2 * time.Second, EndPos: 37, }, }, - PosRange: posrange.PositionRange{ - Start: 14, - End: 38, - }, + PosRange: posrange.PositionRange{Start: 14, End: 38}, }, Range: 5 * time.Minute, EndPos: 43, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 44, - }, + PosRange: posrange.PositionRange{Start: 0, End: 44}, }, Range: 4 * time.Minute, Step: 3 * time.Second, @@ -3340,29 +3712,20 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 19, - End: 33, - }, + PosRange: posrange.PositionRange{Start: 19, End: 33}, }, Range: 2 * time.Second, EndPos: 37, }, }, - PosRange: posrange.PositionRange{ - Start: 14, - End: 38, - }, + PosRange: posrange.PositionRange{Start: 14, End: 38}, }, Range: 5 * time.Minute, OriginalOffset: 4 * time.Minute, EndPos: 53, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 54, - }, + PosRange: posrange.PositionRange{Start: 0, End: 54}, }, Range: 4 * time.Minute, Step: 3 * time.Second, @@ -3386,29 +3749,20 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 19, - End: 33, - }, + PosRange: posrange.PositionRange{Start: 19, End: 33}, }, Range: 2 * time.Second, EndPos: 37, }, }, - PosRange: posrange.PositionRange{ - Start: 14, - End: 38, - }, + PosRange: posrange.PositionRange{Start: 14, End: 38}, }, Range: 5 * time.Minute, Timestamp: makeInt64Pointer(1603775091000), EndPos: 56, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 57, - }, + PosRange: posrange.PositionRange{Start: 0, End: 57}, }, Range: 4 * time.Minute, Step: 3 * time.Second, @@ -3432,29 +3786,20 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "bar", "baz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 19, - End: 33, - }, + PosRange: posrange.PositionRange{Start: 19, End: 33}, }, Range: 2 * time.Second, EndPos: 37, }, }, - PosRange: posrange.PositionRange{ - Start: 14, - End: 38, - }, + PosRange: posrange.PositionRange{Start: 14, End: 38}, }, Range: 5 * time.Minute, Timestamp: makeInt64Pointer(-160377509000), EndPos: 56, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 57, - }, + PosRange: posrange.PositionRange{Start: 0, End: 57}, }, Range: 4 * time.Minute, Step: 3 * time.Second, @@ -3472,16 +3817,10 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 53, - End: 64, - }, + PosRange: posrange.PositionRange{Start: 53, End: 64}, }, Grouping: []string{"and", "by", "avg", "count", "alert", "annotations"}, - PosRange: posrange.PositionRange{ - Start: 0, - End: 65, - }, + PosRange: posrange.PositionRange{Start: 0, End: 65}, }, Range: 30 * time.Minute, Step: 10 * time.Second, @@ -3496,10 +3835,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 21, - }, + PosRange: posrange.PositionRange{Start: 0, End: 21}, OriginalOffset: 1 * time.Minute, }, Range: 10 * time.Minute, @@ -3515,10 +3851,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 0, End: 17}, Timestamp: makeInt64Pointer(123000), }, Range: 10 * time.Minute, @@ -3534,10 +3867,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 27, - }, + PosRange: posrange.PositionRange{Start: 0, End: 27}, Timestamp: makeInt64Pointer(123000), OriginalOffset: 1 * time.Minute, }, @@ -3554,10 +3884,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 27, - }, + PosRange: posrange.PositionRange{Start: 0, End: 27}, Timestamp: makeInt64Pointer(123000), OriginalOffset: 1 * time.Minute, }, @@ -3574,10 +3901,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "some_metric"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 11, - }, + PosRange: posrange.PositionRange{Start: 0, End: 11}, }, Timestamp: makeInt64Pointer(123000), OriginalOffset: 1 * time.Minute, @@ -3600,10 +3924,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 1, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 1, End: 4}, }, RHS: &VectorSelector{ Name: "bar", @@ -3611,16 +3932,10 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "nm", "val"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: posrange.PositionRange{ - Start: 7, - End: 20, - }, + PosRange: posrange.PositionRange{Start: 7, End: 20}, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 21, - }, + PosRange: posrange.PositionRange{Start: 0, End: 21}, }, Range: 5 * time.Minute, EndPos: 26, @@ -3640,10 +3955,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 1, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 1, End: 4}, }, RHS: &VectorSelector{ Name: "bar", @@ -3651,16 +3963,10 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "nm", "val"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, - PosRange: posrange.PositionRange{ - Start: 7, - End: 20, - }, + PosRange: posrange.PositionRange{Start: 7, End: 20}, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 21, - }, + PosRange: posrange.PositionRange{Start: 0, End: 21}, }, Range: 5 * time.Minute, OriginalOffset: 10 * time.Minute, @@ -3681,10 +3987,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 1, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 1, End: 4}, }, RHS: &VectorSelector{ Name: "bar", @@ -3693,16 +3996,10 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "bar"), }, Timestamp: makeInt64Pointer(1234000), - PosRange: posrange.PositionRange{ - Start: 7, - End: 27, - }, + PosRange: posrange.PositionRange{Start: 7, End: 27}, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 28, - }, + PosRange: posrange.PositionRange{Start: 0, End: 28}, }, Range: 5 * time.Minute, Timestamp: makeInt64Pointer(1603775019000), @@ -3710,24 +4007,58 @@ var testExpr = []struct { }, }, { - input: "test[5d] OFFSET 10s [10m:5s]", - fail: true, - errMsg: "1:1: parse error: subquery is only allowed on instant vector, got matrix", + input: "test[5d] OFFSET 10s [10m:5s]", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 28}, + Err: errors.New("subquery is only allowed on instant vector, got matrix instead"), + Query: "test[5d] OFFSET 10s [10m:5s]", + }, + }, }, { - 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: `(foo + bar{nm="val"})[5m:][10m:5s]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 34}, + Err: errors.New(`subquery is only allowed on instant vector, got matrix instead`), + Query: `(foo + bar{nm="val"})[5m:][10m:5s]`, + }, + }, }, { - 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] offset 1h", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 14, End: 18}, + Err: errors.New(`ranges only allowed for vector selectors`), + Query: "rate(food[1m])[1h] offset 1h", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 18}, + Err: errors.New(`ranges only allowed for vector selectors`), + Query: "rate(food[1m])[1h] offset 1h", + }, + }, }, { - input: "rate(food[1m])[1h] @ 100", - fail: true, - errMsg: `1:15: parse error: ranges only allowed for vector selectors`, + input: "rate(food[1m])[1h] @ 100", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 14, End: 18}, + Err: errors.New(`ranges only allowed for vector selectors`), + Query: "rate(food[1m])[1h] @ 100", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 18}, + Err: errors.New(`ranges only allowed for vector selectors`), + Query: "rate(food[1m])[1h] @ 100", + }, + }, }, // Preprocessors. { @@ -3738,10 +4069,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 13, - }, + PosRange: posrange.PositionRange{Start: 0, End: 13}, }, }, { @@ -3752,10 +4080,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 11, - }, + PosRange: posrange.PositionRange{Start: 0, End: 11}, }, }, { @@ -3767,10 +4092,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 5 * 365 * 24 * time.Hour, EndPos: 18, @@ -3785,10 +4107,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "test"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 4, - }, + PosRange: posrange.PositionRange{Start: 0, End: 4}, }, Range: 5 * 365 * 24 * time.Hour, EndPos: 16, @@ -3802,10 +4121,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 10 * time.Minute, Step: 6 * time.Second, @@ -3821,10 +4137,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, Range: 10 * time.Minute, Step: 6 * time.Second, @@ -3833,14 +4146,26 @@ var testExpr = []struct { }, }, { - input: `start()`, - fail: true, - errMsg: `1:6: parse error: unexpected "("`, + input: `start()`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 5, End: 6}, + Err: errors.New(`unexpected "("`), + Query: `start()`, + }, + }, }, { - input: `end()`, - fail: true, - errMsg: `1:4: parse error: unexpected "("`, + input: `end()`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 3, End: 4}, + Err: errors.New(`unexpected "("`), + Query: `end()`, + }, + }, }, // Check that start and end functions do not mask metrics. { @@ -3850,10 +4175,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "start"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 5, - }, + PosRange: posrange.PositionRange{Start: 0, End: 5}, }, }, { @@ -3863,10 +4185,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "end"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, }, { @@ -3877,10 +4196,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "end", "foo"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "start"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 16, - }, + PosRange: posrange.PositionRange{Start: 0, End: 16}, }, }, { @@ -3891,10 +4207,7 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "start", "foo"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "end"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 16, - }, + PosRange: posrange.PositionRange{Start: 0, End: 16}, }, }, { @@ -3906,20 +4219,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 21, End: 24}, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, @@ -3937,20 +4244,14 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + 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, - }, + PosRange: posrange.PositionRange{Start: 19, End: 22}, }, VectorMatching: &VectorMatching{ Card: CardManyToMany, @@ -3965,11 +4266,8 @@ var testExpr = []struct { Func: MustGetFunction("info"), Args: Expressions{ &Call{ - Func: MustGetFunction("rate"), - PosRange: posrange.PositionRange{ - Start: 5, - End: 43, - }, + Func: MustGetFunction("rate"), + PosRange: posrange.PositionRange{Start: 5, End: 43}, Args: Expressions{ &MatrixSelector{ VectorSelector: &VectorSelector{ @@ -3978,10 +4276,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "http_request_counter_total"), }, - PosRange: posrange.PositionRange{ - Start: 10, - End: 38, - }, + PosRange: posrange.PositionRange{Start: 10, End: 38}, }, EndPos: 42, Range: 5 * time.Minute, @@ -3989,16 +4284,19 @@ var testExpr = []struct { }, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 44, - }, + PosRange: posrange.PositionRange{Start: 0, End: 44}, }, }, { - input: `info(rate(http_request_counter_total{}[5m]), target_info{foo="bar"})`, - fail: true, - errMsg: `1:46: parse error: expected label selectors only, got vector selector instead`, + input: `info(rate(http_request_counter_total{}[5m]), target_info{foo="bar"})`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 45, End: 67}, + Err: errors.New(`expected label selectors only, got vector selector instead`), + Query: `info(rate(http_request_counter_total{}[5m]), target_info{foo="bar"})`, + }, + }, }, { input: `info(http_request_counter_total{namespace="zzz"}, {foo="bar", bar="baz"})`, @@ -4011,27 +4309,18 @@ var testExpr = []struct { MustLabelMatcher(labels.MatchEqual, "namespace", "zzz"), MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "http_request_counter_total"), }, - PosRange: posrange.PositionRange{ - Start: 5, - End: 48, - }, + PosRange: posrange.PositionRange{Start: 5, End: 48}, }, &VectorSelector{ LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, "foo", "bar"), MustLabelMatcher(labels.MatchEqual, "bar", "baz"), }, - PosRange: posrange.PositionRange{ - Start: 50, - End: 72, - }, + PosRange: posrange.PositionRange{Start: 50, End: 72}, BypassEmptyMatcherCheck: true, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 73, - }, + PosRange: posrange.PositionRange{Start: 0, End: 73}, }, }, // Test that nested parentheses result in the correct position range. @@ -4043,29 +4332,20 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: SUB, LHS: &DurationExpr{ Op: ADD, LHS: &NumberLiteral{ - Val: 11, - PosRange: posrange.PositionRange{ - Start: 4, - End: 7, - }, + Val: 11, + PosRange: posrange.PositionRange{Start: 4, End: 7}, Duration: true, }, RHS: &NumberLiteral{ - Val: 10, - PosRange: posrange.PositionRange{ - Start: 8, - End: 11, - }, + Val: 10, + PosRange: posrange.PositionRange{Start: 8, End: 11}, Duration: true, }, }, @@ -4090,10 +4370,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: ADD, @@ -4103,30 +4380,21 @@ var testExpr = []struct { RHS: &DurationExpr{ Op: SUB, LHS: &NumberLiteral{ - Val: 10, - PosRange: posrange.PositionRange{ - Start: 6, - End: 9, - }, + Val: 10, + PosRange: posrange.PositionRange{Start: 6, End: 9}, Duration: true, }, RHS: &NumberLiteral{ - Val: 5, - PosRange: posrange.PositionRange{ - Start: 10, - End: 12, - }, + Val: 5, + PosRange: posrange.PositionRange{Start: 10, End: 12}, Duration: true, }, Wrapped: true, }, }, RHS: &NumberLiteral{ - Val: 20, - PosRange: posrange.PositionRange{ - Start: 14, - End: 17, - }, + Val: 20, + PosRange: posrange.PositionRange{Start: 14, End: 17}, Duration: true, }, }, @@ -4141,27 +4409,18 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: ADD, LHS: &NumberLiteral{ - Val: -10, - PosRange: posrange.PositionRange{ - Start: 4, - End: 8, - }, + Val: -10, + PosRange: posrange.PositionRange{Start: 4, End: 8}, Duration: true, }, RHS: &NumberLiteral{ - Val: 15, - PosRange: posrange.PositionRange{ - Start: 9, - End: 12, - }, + Val: 15, + PosRange: posrange.PositionRange{Start: 9, End: 12}, Duration: true, }, }, @@ -4176,10 +4435,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: STEP, @@ -4197,10 +4453,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: SUB, @@ -4222,10 +4475,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: STEP, @@ -4243,10 +4493,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: SUB, @@ -4263,10 +4510,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 0, End: 17}, OriginalOffsetExpr: &DurationExpr{ Op: STEP, StartPos: 11, @@ -4281,10 +4525,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 18, - }, + PosRange: posrange.PositionRange{Start: 0, End: 18}, OriginalOffsetExpr: &DurationExpr{ Op: SUB, StartPos: 11, @@ -4300,10 +4541,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: MAX, @@ -4315,10 +4553,7 @@ var testExpr = []struct { RHS: &NumberLiteral{ Val: 5, Duration: true, - PosRange: posrange.PositionRange{ - Start: 15, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 15, End: 17}, }, StartPos: 4, EndPos: 18, @@ -4333,10 +4568,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 25, - }, + PosRange: posrange.PositionRange{Start: 0, End: 25}, OriginalOffsetExpr: &DurationExpr{ Op: MAX, LHS: &DurationExpr{ @@ -4347,10 +4579,7 @@ var testExpr = []struct { RHS: &NumberLiteral{ Val: 5, Duration: true, - PosRange: posrange.PositionRange{ - Start: 22, - End: 24, - }, + PosRange: posrange.PositionRange{Start: 22, End: 24}, }, StartPos: 11, EndPos: 25, @@ -4364,10 +4593,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 29, - }, + PosRange: posrange.PositionRange{Start: 0, End: 29}, OriginalOffsetExpr: &DurationExpr{ Op: SUB, RHS: &DurationExpr{ @@ -4375,10 +4601,7 @@ var testExpr = []struct { LHS: &NumberLiteral{ Val: 5, Duration: true, - PosRange: posrange.PositionRange{ - Start: 16, - End: 18, - }, + PosRange: posrange.PositionRange{Start: 16, End: 18}, }, RHS: &DurationExpr{ Op: ADD, @@ -4390,10 +4613,7 @@ var testExpr = []struct { RHS: &NumberLiteral{ Val: 8, Duration: true, - PosRange: posrange.PositionRange{ - Start: 26, - End: 28, - }, + PosRange: posrange.PositionRange{Start: 26, End: 28}, }, }, StartPos: 12, @@ -4412,64 +4632,43 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: ADD, LHS: &NumberLiteral{ - Val: 4, - PosRange: posrange.PositionRange{ - Start: 4, - End: 6, - }, + Val: 4, + PosRange: posrange.PositionRange{Start: 4, End: 6}, Duration: true, }, RHS: &NumberLiteral{ - Val: 4, - PosRange: posrange.PositionRange{ - Start: 7, - End: 9, - }, + Val: 4, + PosRange: posrange.PositionRange{Start: 7, End: 9}, Duration: true, }, }, StepExpr: &DurationExpr{ Op: MUL, LHS: &NumberLiteral{ - Val: 1, - PosRange: posrange.PositionRange{ - Start: 10, - End: 12, - }, + Val: 1, + PosRange: posrange.PositionRange{Start: 10, End: 12}, Duration: true, }, RHS: &NumberLiteral{ - Val: 2, - PosRange: posrange.PositionRange{ - Start: 13, - End: 14, - }, + Val: 2, + PosRange: posrange.PositionRange{Start: 13, End: 14}, }, }, OriginalOffsetExpr: &DurationExpr{ Op: SUB, LHS: &NumberLiteral{ - Val: 5, - PosRange: posrange.PositionRange{ - Start: 24, - End: 26, - }, + Val: 5, + PosRange: posrange.PositionRange{Start: 24, End: 26}, Duration: true, }, RHS: &NumberLiteral{ - Val: 8, - PosRange: posrange.PositionRange{ - Start: 27, - End: 28, - }, + Val: 8, + PosRange: posrange.PositionRange{Start: 27, End: 28}, }, Wrapped: true, }, @@ -4486,17 +4685,11 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 13, - }, + PosRange: posrange.PositionRange{Start: 0, End: 13}, }, RHS: &NumberLiteral{ - Val: 8, - PosRange: posrange.PositionRange{ - Start: 14, - End: 15, - }, + Val: 8, + PosRange: posrange.PositionRange{Start: 14, End: 15}, }, }, }, @@ -4511,37 +4704,25 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 5, - End: 8, - }, + PosRange: posrange.PositionRange{Start: 5, End: 8}, }, RangeExpr: &DurationExpr{ Op: ADD, LHS: &NumberLiteral{ - Val: 120, - PosRange: posrange.PositionRange{ - Start: 9, - End: 11, - }, + Val: 120, + PosRange: posrange.PositionRange{Start: 9, End: 11}, Duration: true, }, RHS: &NumberLiteral{ - Val: 120, - PosRange: posrange.PositionRange{ - Start: 12, - End: 14, - }, + Val: 120, + PosRange: posrange.PositionRange{Start: 12, End: 14}, Duration: true, }, }, EndPos: 15, }, }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 16, - }, + PosRange: posrange.PositionRange{Start: 0, End: 16}, }, }, { @@ -4554,17 +4735,11 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 13, - }, + PosRange: posrange.PositionRange{Start: 0, End: 13}, }, RHS: &NumberLiteral{ - Val: 1, - PosRange: posrange.PositionRange{ - Start: 14, - End: 15, - }, + Val: 1, + PosRange: posrange.PositionRange{Start: 14, End: 15}, }, }, }, @@ -4578,18 +4753,12 @@ var testExpr = []struct { RHS: &DurationExpr{ Op: POW, LHS: &NumberLiteral{ - Val: 1, - PosRange: posrange.PositionRange{ - Start: 13, - End: 14, - }, + Val: 1, + PosRange: posrange.PositionRange{Start: 13, End: 14}, }, RHS: &NumberLiteral{ - Val: 2, - PosRange: posrange.PositionRange{ - Start: 15, - End: 16, - }, + Val: 2, + PosRange: posrange.PositionRange{Start: 15, End: 16}, }, Wrapped: true, }, @@ -4598,10 +4767,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 0, End: 17}, }, }, { @@ -4614,18 +4780,12 @@ var testExpr = []struct { RHS: &DurationExpr{ Op: POW, LHS: &NumberLiteral{ - Val: 1, - PosRange: posrange.PositionRange{ - Start: 13, - End: 14, - }, + Val: 1, + PosRange: posrange.PositionRange{Start: 13, End: 14}, }, RHS: &NumberLiteral{ - Val: 2, - PosRange: posrange.PositionRange{ - Start: 15, - End: 16, - }, + Val: 2, + PosRange: posrange.PositionRange{Start: 15, End: 16}, }, }, Wrapped: true, @@ -4634,10 +4794,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 17, - }, + PosRange: posrange.PositionRange{Start: 0, End: 17}, }, }, { @@ -4648,10 +4805,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: SUB, @@ -4659,18 +4813,12 @@ var testExpr = []struct { RHS: &DurationExpr{ Op: POW, LHS: &NumberLiteral{ - Val: 2, - PosRange: posrange.PositionRange{ - Start: 5, - End: 6, - }, + Val: 2, + PosRange: posrange.PositionRange{Start: 5, End: 6}, }, RHS: &NumberLiteral{ - Val: 2, - PosRange: posrange.PositionRange{ - Start: 7, - End: 8, - }, + Val: 2, + PosRange: posrange.PositionRange{Start: 7, End: 8}, }, }, StartPos: 4, @@ -4686,19 +4834,13 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 0, - End: 3, - }, + PosRange: posrange.PositionRange{Start: 0, End: 3}, }, RangeExpr: &DurationExpr{ Op: ADD, LHS: &NumberLiteral{ - Val: 0, - PosRange: posrange.PositionRange{ - Start: 4, - End: 5, - }, + Val: 0, + PosRange: posrange.PositionRange{Start: 4, End: 5}, }, RHS: &DurationExpr{ Op: SUB, @@ -4706,18 +4848,12 @@ var testExpr = []struct { RHS: &DurationExpr{ Op: POW, LHS: &NumberLiteral{ - Val: 2, - PosRange: posrange.PositionRange{ - Start: 7, - End: 8, - }, + Val: 2, + PosRange: posrange.PositionRange{Start: 7, End: 8}, }, RHS: &NumberLiteral{ - Val: 2, - PosRange: posrange.PositionRange{ - Start: 9, - End: 10, - }, + Val: 2, + PosRange: posrange.PositionRange{Start: 9, End: 10}, }, }, StartPos: 6, @@ -4727,39 +4863,99 @@ var testExpr = []struct { }, }, { - input: `foo[step]`, - fail: true, - errMsg: `1:9: parse error: unexpected "]" in subquery or range selector, expected number, duration, or step()`, + input: `foo[step]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 8, End: 9}, + Err: errors.New(`unexpected "]" in subquery or range selector, expected number, duration, or step()`), + Query: `foo[step]`, + }, + }, }, { - input: `foo[step()/0d]`, - fail: true, - errMsg: `division by zero`, + input: `foo[step()/0d]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 10, End: 11}, + Err: errors.New(`division by zero`), + Query: `foo[step()/0d]`, + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 0}, // FIXME: this position looks wrong. + Err: errors.New(`duration must be greater than 0`), + Query: `foo[step()/0d]`, + }, + }, }, { - input: `foo[5s/0d]`, - fail: true, - errMsg: `division by zero`, + input: `foo[5s/0d]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 6, End: 7}, + Err: errors.New(`division by zero`), + Query: `foo[5s/0d]`, + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 0}, // FIXME: this position looks wrong. + Err: errors.New(`duration must be greater than 0`), + Query: `foo[5s/0d]`, + }, + }, }, { - input: `foo offset (4d/0)`, - fail: true, - errMsg: `division by zero`, + input: `foo offset (4d/0)`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 14, End: 15}, + Err: errors.New(`division by zero`), + Query: `foo offset (4d/0)`, + }, + }, }, { - input: `foo[5s%0d]`, - fail: true, - errMsg: `modulo by zero`, + input: `foo[5s%0d]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 6, End: 7}, + Err: errors.New(`modulo by zero`), + Query: `foo[5s%0d]`, + }, + ParseErr{ + Err: errors.New(`duration must be greater than 0`), + Query: `foo[5s%0d]`, + }, + }, }, { - input: `foo offset 9.5e10`, - fail: true, - errMsg: `duration out of range`, + input: `foo offset 9.5e10`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 11, End: 17}, + Err: errors.New(`duration out of range`), + Query: `foo offset 9.5e10`, + }, + }, }, { - input: `foo[9.5e10]`, - fail: true, - errMsg: `duration out of range`, + input: `foo[9.5e10]`, + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 10}, + Err: errors.New(`duration out of range`), + Query: `foo[9.5e10]`, + }, + ParseErr{ + Err: errors.New(`duration must be greater than 0`), + Query: `foo[9.5e10]`, + }, + }, }, { input: "(sum(foo))", @@ -4771,10 +4967,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 5, - End: 8, - }, + PosRange: posrange.PositionRange{Start: 5, End: 8}, }, PosRange: posrange.PositionRange{Start: 1, End: 9}, }, @@ -4812,10 +5005,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 5, - End: 8, - }, + PosRange: posrange.PositionRange{Start: 5, End: 8}, }, PosRange: posrange.PositionRange{Start: 1, End: 18}, }, @@ -4833,10 +5023,7 @@ var testExpr = []struct { LabelMatchers: []*labels.Matcher{ MustLabelMatcher(labels.MatchEqual, model.MetricNameLabel, "foo"), }, - PosRange: posrange.PositionRange{ - Start: 15, - End: 18, - }, + PosRange: posrange.PositionRange{Start: 15, End: 18}, }, PosRange: posrange.PositionRange{Start: 1, End: 19}, }, @@ -4872,19 +5059,58 @@ var testExpr = []struct { }, }, { - input: "sum(rate(", - fail: true, - errMsg: "unclosed left parenthesis", + input: "sum(", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 4, End: 4}, + Err: errors.New("unclosed left parenthesis"), + Query: "sum(", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 0}, // FIXME: this position looks wrong. + Err: errors.New("no arguments for aggregate expression provided"), + Query: "sum(", + }, + }, }, { - input: "foo[5s x 5s]", - fail: true, - errMsg: "unexpected character: 'x', expected ':'", + input: "sum(rate(", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 9, End: 9}, + Err: errors.New("unclosed left parenthesis"), + Query: "sum(rate(", + }, + ParseErr{ + PositionRange: posrange.PositionRange{Start: 0, End: 0}, // FIXME: this position looks wrong. + Err: errors.New("no arguments for aggregate expression provided"), + Query: "sum(rate(", + }, + }, }, { - input: "foo[5s s 5s]", - fail: true, - errMsg: "unexpected character: 's', expected ':'", + input: "foo[5s x 5s]", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 7, End: 12}, + Err: errors.New("unexpected character: 'x', expected ':'"), + Query: "foo[5s x 5s]", + }, + }, + }, + { + input: "foo[5s s 5s]", + fail: true, + errors: ParseErrors{ + ParseErr{ + PositionRange: posrange.PositionRange{Start: 7, End: 12}, + Err: errors.New("unexpected character: 's', expected ':'"), + Query: "foo[5s s 5s]", + }, + }, }, } @@ -4948,13 +5174,16 @@ func TestParseExpressions(t *testing.T) { require.Equal(t, expected, expr, "error on input '%s'", test.input) } else { require.Error(t, err) - require.ErrorContains(t, err, test.errMsg, "unexpected error on input '%s', expected '%s', got '%s'", test.input, test.errMsg, err.Error()) var errorList ParseErrors ok := errors.As(err, &errorList) require.True(t, ok, "unexpected error type") + if diff := cmp.Diff(test.errors, errorList, equalParseErr()); diff != "" { + t.Errorf("mismatch (-want +got):\n%s\nErrors: %+v", diff, errorList) + } + 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) @@ -4965,6 +5194,22 @@ func TestParseExpressions(t *testing.T) { } } +// Two errors are identical if they return the exact same string from Error() call. +func equalParseErr() cmp.Option { + return cmp.Comparer(func(a, b ParseErr) bool { + if a.Query != b.Query { + return false + } + if a.LineOffset != b.LineOffset { + return false + } + if !cmp.Equal(a.PositionRange, b.PositionRange) { + return false + } + return a.Err.Error() == b.Err.Error() + }) +} + func TestParseSeriesDesc(t *testing.T) { tests := []struct { name string