diff --git a/promql/lex.go b/promql/lex.go index 5bbe843646..c3fd4e65e2 100644 --- a/promql/lex.go +++ b/promql/lex.go @@ -889,3 +889,16 @@ func isDigit(r rune) bool { func isAlpha(r rune) bool { return r == '_' || ('a' <= r && r <= 'z') || ('A' <= r && r <= 'Z') } + +// isLabel reports whether the string can be used as label. +func isLabel(s string) bool { + if len(s) == 0 || !isAlpha(rune(s[0])) { + return false + } + for _, c := range s[1:] { + if !isAlphaNumeric(c) { + return false + } + } + return true +} diff --git a/promql/parse.go b/promql/parse.go index dbdc662ec5..6acaa11de3 100644 --- a/promql/parse.go +++ b/promql/parse.go @@ -673,7 +673,10 @@ func (p *parser) labels() model.LabelNames { labels := model.LabelNames{} if p.peek().typ != itemRightParen { for { - id := p.expect(itemIdentifier, ctx) + id := p.next() + if !isLabel(id.val) { + p.errorf("unexpected %s in %s, expected label", id.desc(), ctx) + } labels = append(labels, model.LabelName(id.val)) if p.peek().typ != itemComma { diff --git a/promql/parse_test.go b/promql/parse_test.go index 901c99ab3a..191cd39e17 100644 --- a/promql/parse_test.go +++ b/promql/parse_test.go @@ -1225,6 +1225,24 @@ var testExpr = []struct { }, Param: &StringLiteral{"value"}, }, + }, { + // Test usage of keywords as label names. + input: "sum without(and, by, avg, count, alert, annotations)(some_metric)", + expected: &AggregateExpr{ + Op: itemSum, + Without: true, + Expr: &VectorSelector{ + Name: "some_metric", + LabelMatchers: metric.LabelMatchers{ + mustLabelMatcher(metric.Equal, model.MetricNameLabel, "some_metric"), + }, + }, + Grouping: model.LabelNames{"and", "by", "avg", "count", "alert", "annotations"}, + }, + }, { + input: "sum without(==)(some_metric)", + fail: true, + errMsg: "unexpected in grouping opts, expected label", }, { input: `sum some_metric by (test)`, fail: true,