mirror of
https://github.com/prometheus/prometheus.git
synced 2024-12-25 05:34:05 -08:00
Quote label name in matchers when needed
When the label name of a matcher contains non-standard characters, like a dot, or starts with a digit, it should be quoted. If it's not quoted, then `VectorSelector.String()` isn't a valid PromQL. Signed-off-by: Oleg Zaytsev <mail@olegzaytsev.com>
This commit is contained in:
parent
0ba747187d
commit
dabd789fd5
|
@ -78,7 +78,20 @@ func MustNewMatcher(mt MatchType, name, val string) *Matcher {
|
|||
}
|
||||
|
||||
func (m *Matcher) String() string {
|
||||
return fmt.Sprintf("%s%s%q", m.Name, m.Type, m.Value)
|
||||
if !m.shouldQuoteName() {
|
||||
return fmt.Sprintf("%s%s%q", m.Name, m.Type, m.Value)
|
||||
}
|
||||
return fmt.Sprintf("%q%s%q", m.Name, m.Type, m.Value)
|
||||
}
|
||||
|
||||
func (m *Matcher) shouldQuoteName() bool {
|
||||
for i, c := range m.Name {
|
||||
if c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (i > 0 && c >= '0' && c <= '9') {
|
||||
continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Matches returns whether the matcher matches the given string value.
|
||||
|
|
|
@ -15,6 +15,7 @@ package labels
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -225,3 +226,128 @@ func BenchmarkNewMatcher(b *testing.B) {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkMatcher_String(b *testing.B) {
|
||||
type benchCase struct {
|
||||
name string
|
||||
matchers []*Matcher
|
||||
}
|
||||
cases := []benchCase{
|
||||
{
|
||||
name: "short name equal",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchEqual, "foo", "bar"),
|
||||
MustNewMatcher(MatchEqual, "bar", "baz"),
|
||||
MustNewMatcher(MatchEqual, "abc", "def"),
|
||||
MustNewMatcher(MatchEqual, "ghi", "klm"),
|
||||
MustNewMatcher(MatchEqual, "nop", "qrs"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "short quoted name not equal",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchEqual, "f.o", "bar"),
|
||||
MustNewMatcher(MatchEqual, "b.r", "baz"),
|
||||
MustNewMatcher(MatchEqual, "a.c", "def"),
|
||||
MustNewMatcher(MatchEqual, "g.i", "klm"),
|
||||
MustNewMatcher(MatchEqual, "n.p", "qrs"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "short quoted name with quotes not equal",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchEqual, `"foo"`, "bar"),
|
||||
MustNewMatcher(MatchEqual, `"foo"`, "baz"),
|
||||
MustNewMatcher(MatchEqual, `"foo"`, "def"),
|
||||
MustNewMatcher(MatchEqual, `"foo"`, "klm"),
|
||||
MustNewMatcher(MatchEqual, `"foo"`, "qrs"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "short name value with quotes equal",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchEqual, "foo", `"bar"`),
|
||||
MustNewMatcher(MatchEqual, "bar", `"baz"`),
|
||||
MustNewMatcher(MatchEqual, "abc", `"def"`),
|
||||
MustNewMatcher(MatchEqual, "ghi", `"klm"`),
|
||||
MustNewMatcher(MatchEqual, "nop", `"qrs"`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "short name and long value regexp",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchRegexp, "foo", "five_six_seven_eight_nine_ten_one_two_three_four"),
|
||||
MustNewMatcher(MatchRegexp, "bar", "one_two_three_four_five_six_seven_eight_nine_ten"),
|
||||
MustNewMatcher(MatchRegexp, "abc", "two_three_four_five_six_seven_eight_nine_ten_one"),
|
||||
MustNewMatcher(MatchRegexp, "ghi", "three_four_five_six_seven_eight_nine_ten_one_two"),
|
||||
MustNewMatcher(MatchRegexp, "nop", "four_five_six_seven_eight_nine_ten_one_two_three"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "short name and long value with quotes equal",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchEqual, "foo", `five_six_seven_eight_nine_ten_"one"_two_three_four`),
|
||||
MustNewMatcher(MatchEqual, "bar", `one_two_three_four_five_six_"seven"_eight_nine_ten`),
|
||||
MustNewMatcher(MatchEqual, "abc", `two_three_four_five_six_seven_"eight"_nine_ten_one`),
|
||||
MustNewMatcher(MatchEqual, "ghi", `three_four_five_six_seven_eight_"nine"_ten_one_two`),
|
||||
MustNewMatcher(MatchEqual, "nop", `four_five_six_seven_eight_nine_"ten"_one_two_three`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "long name regexp",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchRegexp, "one_two_three_four_five_six_seven_eight_nine_ten", "val"),
|
||||
MustNewMatcher(MatchRegexp, "two_three_four_five_six_seven_eight_nine_ten_one", "val"),
|
||||
MustNewMatcher(MatchRegexp, "three_four_five_six_seven_eight_nine_ten_one_two", "val"),
|
||||
MustNewMatcher(MatchRegexp, "four_five_six_seven_eight_nine_ten_one_two_three", "val"),
|
||||
MustNewMatcher(MatchRegexp, "five_six_seven_eight_nine_ten_one_two_three_four", "val"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "long quoted name regexp",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchRegexp, "one.two.three.four.five.six.seven.eight.nine.ten", "val"),
|
||||
MustNewMatcher(MatchRegexp, "two.three.four.five.six.seven.eight.nine.ten.one", "val"),
|
||||
MustNewMatcher(MatchRegexp, "three.four.five.six.seven.eight.nine.ten.one.two", "val"),
|
||||
MustNewMatcher(MatchRegexp, "four.five.six.seven.eight.nine.ten.one.two.three", "val"),
|
||||
MustNewMatcher(MatchRegexp, "five.six.seven.eight.nine.ten.one.two.three.four", "val"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "long name and long value regexp",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchRegexp, "one_two_three_four_five_six_seven_eight_nine_ten", "five_six_seven_eight_nine_ten_one_two_three_four"),
|
||||
MustNewMatcher(MatchRegexp, "two_three_four_five_six_seven_eight_nine_ten_one", "one_two_three_four_five_six_seven_eight_nine_ten"),
|
||||
MustNewMatcher(MatchRegexp, "three_four_five_six_seven_eight_nine_ten_one_two", "two_three_four_five_six_seven_eight_nine_ten_one"),
|
||||
MustNewMatcher(MatchRegexp, "four_five_six_seven_eight_nine_ten_one_two_three", "three_four_five_six_seven_eight_nine_ten_one_two"),
|
||||
MustNewMatcher(MatchRegexp, "five_six_seven_eight_nine_ten_one_two_three_four", "four_five_six_seven_eight_nine_ten_one_two_three"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "long quoted name and long value regexp",
|
||||
matchers: []*Matcher{
|
||||
MustNewMatcher(MatchRegexp, "one.two.three.four.five.six.seven.eight.nine.ten", "five.six.seven.eight.nine.ten.one.two.three.four"),
|
||||
MustNewMatcher(MatchRegexp, "two.three.four.five.six.seven.eight.nine.ten.one", "one.two.three.four.five.six.seven.eight.nine.ten"),
|
||||
MustNewMatcher(MatchRegexp, "three.four.five.six.seven.eight.nine.ten.one.two", "two.three.four.five.six.seven.eight.nine.ten.one"),
|
||||
MustNewMatcher(MatchRegexp, "four.five.six.seven.eight.nine.ten.one.two.three", "three.four.five.six.seven.eight.nine.ten.one.two"),
|
||||
MustNewMatcher(MatchRegexp, "five.six.seven.eight.nine.ten.one.two.three.four", "four.five.six.seven.eight.nine.ten.one.two.three"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var mixed []*Matcher
|
||||
for _, bc := range cases {
|
||||
mixed = append(mixed, bc.matchers...)
|
||||
}
|
||||
rand.Shuffle(len(mixed), func(i, j int) { mixed[i], mixed[j] = mixed[j], mixed[i] })
|
||||
cases = append(cases, benchCase{name: "mixed", matchers: mixed})
|
||||
|
||||
for _, bc := range cases {
|
||||
b.Run(bc.name, func(b *testing.B) {
|
||||
for i := 0; i <= b.N; i++ {
|
||||
m := bc.matchers[i%len(bc.matchers)]
|
||||
_ = m.String()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,6 +138,16 @@ func TestExprString(t *testing.T) {
|
|||
{
|
||||
in: `{__name__="",a="x"}`,
|
||||
},
|
||||
{
|
||||
in: `{"a.b"="c"}`,
|
||||
},
|
||||
{
|
||||
in: `{"0"="1"}`,
|
||||
},
|
||||
{
|
||||
in: `{"_0"="1"}`,
|
||||
out: `{_0="1"}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range inputs {
|
||||
|
|
Loading…
Reference in a new issue