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