Fix panic: runtime error: index out of range

If the measurement started with a quote, a panic would happen.  This
is a reegression due to cb7f0b8.

This also uncovered that measurement names were being escaped incorrectly.
The escape codes for tag and fields also includes `=` and '"` which should
not be escaped for measurement names.

Fixes #3681
pull/3716/head
Jason Wilder 2015-08-18 10:24:03 -06:00
parent 4eb48b5a18
commit 329a0cdb74
2 changed files with 37 additions and 4 deletions

View File

@ -91,6 +91,11 @@ var (
} }
escapeCodesStr = map[string]string{} escapeCodesStr = map[string]string{}
measurementEscapeCodes = map[byte][]byte{
',': []byte(`\,`),
' ': []byte(`\ `),
}
) )
func init() { func init() {
@ -707,7 +712,7 @@ func scanLine(buf []byte, i int) (int, []byte) {
} }
// If we see a double quote, makes sure it is not escaped // If we see a double quote, makes sure it is not escaped
if buf[i] == '"' && buf[i-1] != '\\' { if buf[i] == '"' && (i-1 > 0 && buf[i-1] != '\\') {
i += 1 i += 1
quoted = !quoted quoted = !quoted
continue continue
@ -827,6 +832,20 @@ func scanFieldValue(buf []byte, i int) (int, []byte) {
return i, buf[start:i] return i, buf[start:i]
} }
func escapeMeasurement(in []byte) []byte {
for b, esc := range measurementEscapeCodes {
in = bytes.Replace(in, []byte{b}, esc, -1)
}
return in
}
func unescapeMeasurement(in []byte) []byte {
for b, esc := range measurementEscapeCodes {
in = bytes.Replace(in, esc, []byte{b}, -1)
}
return in
}
func escape(in []byte) []byte { func escape(in []byte) []byte {
for b, esc := range escapeCodes { for b, esc := range escapeCodes {
in = bytes.Replace(in, []byte{b}, esc, -1) in = bytes.Replace(in, []byte{b}, esc, -1)
@ -948,7 +967,7 @@ func (p *point) Tags() Tags {
func MakeKey(name []byte, tags Tags) []byte { func MakeKey(name []byte, tags Tags) []byte {
// unescape the name and then re-escape it to avoid double escaping. // unescape the name and then re-escape it to avoid double escaping.
// The key should always be stored in escaped form. // The key should always be stored in escaped form.
return append(escape(unescape(name)), tags.HashKey()...) return append(escapeMeasurement(unescapeMeasurement(name)), tags.HashKey()...)
} }
// SetTags replaces the tags for the point // SetTags replaces the tags for the point

View File

@ -740,7 +740,22 @@ func TestParsePointWithStringWithCommas(t *testing.T) {
}, },
time.Unix(1, 0)), time.Unix(1, 0)),
) )
}
func TestParsePointQuotedMeasurement(t *testing.T) {
// non-escaped comma
test(t, `"cpu",host=serverA,region=us-east value=1.0 1000000000`,
tsdb.NewPoint(
`"cpu"`,
tsdb.Tags{
"host": "serverA",
"region": "us-east",
},
tsdb.Fields{
"value": 1.0,
},
time.Unix(1, 0)),
)
} }
func TestParsePointEscapedStringsAndCommas(t *testing.T) { func TestParsePointEscapedStringsAndCommas(t *testing.T) {
@ -771,7 +786,6 @@ func TestParsePointEscapedStringsAndCommas(t *testing.T) {
}, },
time.Unix(1, 0)), time.Unix(1, 0)),
) )
} }
func TestParsePointWithStringWithEquals(t *testing.T) { func TestParsePointWithStringWithEquals(t *testing.T) {
@ -1193,7 +1207,7 @@ func TestNewPointEscaped(t *testing.T) {
// equals // equals
pt = tsdb.NewPoint("cpu=main", tsdb.Tags{"tag=bar": "value=foo"}, tsdb.Fields{"name=bar": 1.0}, time.Unix(0, 0)) pt = tsdb.NewPoint("cpu=main", tsdb.Tags{"tag=bar": "value=foo"}, tsdb.Fields{"name=bar": 1.0}, time.Unix(0, 0))
if exp := `cpu\=main,tag\=bar=value\=foo name\=bar=1.0 0`; pt.String() != exp { if exp := `cpu=main,tag\=bar=value\=foo name\=bar=1.0 0`; pt.String() != exp {
t.Errorf("NewPoint().String() mismatch.\ngot %v\nexp %v", pt.String(), exp) t.Errorf("NewPoint().String() mismatch.\ngot %v\nexp %v", pt.String(), exp)
} }
} }