Fix string field value escaping

Commas and quotes could get escaped and parsed incorrectly if they
were both present in a string value.

Fixes #3013
pull/3088/head
Jason Wilder 2015-06-22 12:22:03 -06:00
parent cb9a40df64
commit 2854108941
2 changed files with 55 additions and 4 deletions

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"hash/fnv" "hash/fnv"
"math" "math"
"regexp"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@ -55,6 +56,9 @@ type point struct {
data []byte data []byte
} }
// Compile the regex that detects unquoted double quote sequences
var quoteReplacer = regexp.MustCompile(`([^\\])"`)
var escapeCodes = map[byte][]byte{ var escapeCodes = map[byte][]byte{
',': []byte(`\,`), ',': []byte(`\,`),
'"': []byte(`\"`), '"': []byte(`\"`),
@ -604,7 +608,8 @@ func scanFieldValue(buf []byte, i int) (int, []byte) {
break break
} }
if buf[i] == '"' { // If we see a double quote, makes sure it is not escaped
if buf[i] == '"' && buf[i-1] != '\\' {
i += 1 i += 1
quoted = !quoted quoted = !quoted
continue continue
@ -651,6 +656,21 @@ func unescapeString(in string) string {
return in return in
} }
// escapeQuoteString returns a copy of in with any double quotes that
// have not been escaped with escaped quotes
func escapeQuoteString(in string) string {
if strings.IndexAny(in, `"`) == -1 {
return in
}
return quoteReplacer.ReplaceAllString(in, `$1\"`)
}
// unescapeQuoteString returns a copy of in with any escaped double-quotes
// with unescaped double quotes
func unescapeQuoteString(in string) string {
return strings.Replace(in, `\"`, `"`, -1)
}
// NewPoint returns a new point with the given measurement name, tags, fiels and timestamp // NewPoint returns a new point with the given measurement name, tags, fiels and timestamp
func NewPoint(name string, tags Tags, fields Fields, time time.Time) Point { func NewPoint(name string, tags Tags, fields Fields, time time.Time) Point {
return &point{ return &point{
@ -895,7 +915,7 @@ func newFieldsFromBinary(buf []byte) Fields {
// If the first char is a double-quote, then unmarshal as string // If the first char is a double-quote, then unmarshal as string
if valueBuf[0] == '"' { if valueBuf[0] == '"' {
value = unescapeString(string(valueBuf[1 : len(valueBuf)-1])) value = unescapeQuoteString(string(valueBuf[1 : len(valueBuf)-1]))
// Check for numeric characters // Check for numeric characters
} else if (valueBuf[0] >= '0' && valueBuf[0] <= '9') || valueBuf[0] == '-' || valueBuf[0] == '.' { } else if (valueBuf[0] >= '0' && valueBuf[0] <= '9') || valueBuf[0] == '-' || valueBuf[0] == '.' {
value, err = parseNumber(valueBuf) value, err = parseNumber(valueBuf)
@ -955,7 +975,7 @@ func (p Fields) MarshalBinary() []byte {
b = append(b, t...) b = append(b, t...)
case string: case string:
b = append(b, '"') b = append(b, '"')
b = append(b, []byte(t)...) b = append(b, []byte(escapeQuoteString(t))...)
b = append(b, '"') b = append(b, '"')
case nil: case nil:
// skip // skip

View File

@ -453,7 +453,7 @@ func TestParsePointWithStringWithCommas(t *testing.T) {
}, },
Fields{ Fields{
"value": 1.0, "value": 1.0,
"str": "foo,bar", // commas in string value "str": `foo\,bar`, // commas in string value
}, },
time.Unix(1, 0)), time.Unix(1, 0)),
) )
@ -475,6 +475,37 @@ func TestParsePointWithStringWithCommas(t *testing.T) {
} }
func TestParsePointEscapedStringsAndCommas(t *testing.T) {
// non-escaped comma and quotes
test(t, `cpu,host=serverA,region=us-east value="{Hello\"{,}\" World}" 1000000000`,
NewPoint(
"cpu",
Tags{
"host": "serverA",
"region": "us-east",
},
Fields{
"value": `{Hello"{,}" World}`,
},
time.Unix(1, 0)),
)
// escaped comma and quotes
test(t, `cpu,host=serverA,region=us-east value="{Hello\"{\,}\" World}" 1000000000`,
NewPoint(
"cpu",
Tags{
"host": "serverA",
"region": "us-east",
},
Fields{
"value": `{Hello"{\,}" World}`,
},
time.Unix(1, 0)),
)
}
func TestParsePointWithStringWithEquals(t *testing.T) { func TestParsePointWithStringWithEquals(t *testing.T) {
test(t, `cpu,host=serverA,region=us-east str="foo=bar",value=1.0, 1000000000`, test(t, `cpu,host=serverA,region=us-east str="foo=bar",value=1.0, 1000000000`,
NewPoint( NewPoint(