Merge pull request #2939 from influxdb/jw-line-parse
Return error when parsing invalid field values for line protocolpull/2947/head
commit
86a71a073f
155
tsdb/points.go
155
tsdb/points.go
|
@ -95,7 +95,7 @@ func ParsePointsWithPrecision(buf []byte, defaultTime time.Time, precision strin
|
|||
|
||||
pt, err := parsePoint(block, defaultTime, precision)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to parse '%s': %v", string(block), err)
|
||||
}
|
||||
points = append(points, pt)
|
||||
|
||||
|
@ -318,6 +318,38 @@ func scanFields(buf []byte, i int) (int, []byte, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
// If we see an =, ensure that there is at least on char after it
|
||||
if buf[i] == '=' && !quoted {
|
||||
// check for "... value="
|
||||
if i+1 >= len(buf) {
|
||||
return i, buf[start:i], fmt.Errorf("missing field value")
|
||||
}
|
||||
|
||||
// check for "... value=,value2=..."
|
||||
if buf[i+1] == ',' || buf[i+1] == ' ' {
|
||||
return i, buf[start:i], fmt.Errorf("missing field value")
|
||||
}
|
||||
|
||||
if isNumeric(buf[i+1]) || buf[i+1] == '-' {
|
||||
var err error
|
||||
i, _, err = scanNumber(buf, i+1)
|
||||
if err != nil {
|
||||
return i, buf[start:i], err
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
// If next byte is not a double-quote, the value must be a boolean
|
||||
} else if buf[i+1] != '"' {
|
||||
var err error
|
||||
i, _, err = scanBoolean(buf, i+1)
|
||||
if err != nil {
|
||||
return i, buf[start:i], err
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reached end of block?
|
||||
if buf[i] == ' ' && !quoted {
|
||||
break
|
||||
|
@ -357,6 +389,121 @@ func scanTime(buf []byte, i int) (int, []byte, error) {
|
|||
return i, buf[start:i], nil
|
||||
}
|
||||
|
||||
func isNumeric(b byte) bool {
|
||||
return (b >= '0' && b <= '9') || b == '.'
|
||||
}
|
||||
|
||||
// scanNumber returns the end position within buf, start at i after
|
||||
// scanning over buf for an integer, or float. It returns an
|
||||
// error if a invalid number is scanned.
|
||||
func scanNumber(buf []byte, i int) (int, []byte, error) {
|
||||
start := i
|
||||
|
||||
// Is negative number?
|
||||
if i < len(buf) && buf[i] == '-' {
|
||||
i += 1
|
||||
}
|
||||
|
||||
decimals := 0
|
||||
|
||||
for {
|
||||
if i >= len(buf) {
|
||||
break
|
||||
}
|
||||
|
||||
if buf[i] == ',' || buf[i] == ' ' {
|
||||
break
|
||||
}
|
||||
|
||||
if buf[i] == '.' {
|
||||
decimals += 1
|
||||
}
|
||||
|
||||
// Can't have more than 1 decimal (e.g. 1.1.1 should fail)
|
||||
if decimals > 1 {
|
||||
return i, buf[start:i], fmt.Errorf("invalid number")
|
||||
}
|
||||
|
||||
// `e` is valid for floats but not as the first char
|
||||
if i > start && (buf[i] == 'e') {
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
// + and - are only valid at this point if they follow an e (scientific notation)
|
||||
if (buf[i] == '+' || buf[i] == '-') && buf[i-1] == 'e' {
|
||||
i += 1
|
||||
continue
|
||||
}
|
||||
|
||||
if !isNumeric(buf[i]) {
|
||||
return i, buf[start:i], fmt.Errorf("invalid number")
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
return i, buf[start:i], nil
|
||||
}
|
||||
|
||||
// scanBoolean returns the end position within buf, start at i after
|
||||
// scanning over buf for boolean. Valid values for a boolean are
|
||||
// t, T, true, TRUE, f, F, false, FALSE. It returns an error if a invalid boolean
|
||||
// is scanned.
|
||||
func scanBoolean(buf []byte, i int) (int, []byte, error) {
|
||||
start := i
|
||||
|
||||
if i < len(buf) && (buf[i] != 't' && buf[i] != 'f' && buf[i] != 'T' && buf[i] != 'F') {
|
||||
return i, buf[start:i], fmt.Errorf("invalid boolean")
|
||||
}
|
||||
|
||||
i += 1
|
||||
for {
|
||||
if i >= len(buf) {
|
||||
break
|
||||
}
|
||||
|
||||
if buf[i] == ',' || buf[i] == ' ' {
|
||||
break
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
// Single char bool (t, T, f, F) is ok
|
||||
if i-start == 1 {
|
||||
return i, buf[start:i], nil
|
||||
}
|
||||
|
||||
// length must be 4 for true or TRUE
|
||||
if (buf[start] == 't' || buf[start] == 'T') && i-start != 4 {
|
||||
return i, buf[start:i], fmt.Errorf("invalid boolean")
|
||||
}
|
||||
|
||||
// length must be 5 for false or FALSE
|
||||
if (buf[start] == 'f' || buf[start] == 'F') && i-start != 5 {
|
||||
return i, buf[start:i], fmt.Errorf("invalid boolean")
|
||||
}
|
||||
|
||||
// Otherwise
|
||||
valid := false
|
||||
switch buf[start] {
|
||||
case 't':
|
||||
valid = bytes.Equal(buf[start:i], []byte("true"))
|
||||
case 'f':
|
||||
valid = bytes.Equal(buf[start:i], []byte("false"))
|
||||
case 'T':
|
||||
valid = bytes.Equal(buf[start:i], []byte("TRUE"))
|
||||
case 'F':
|
||||
valid = bytes.Equal(buf[start:i], []byte("FALSE"))
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return i, buf[start:i], fmt.Errorf("invalid boolean")
|
||||
}
|
||||
|
||||
return i, buf[start:i], nil
|
||||
|
||||
}
|
||||
|
||||
// skipWhitespace returns the end position within buf, starting at i after
|
||||
// scanning over spaces in tags
|
||||
func skipWhitespace(buf []byte, i int) int {
|
||||
|
@ -740,16 +887,14 @@ func newFieldsFromBinary(buf []byte) Fields {
|
|||
} else if (valueBuf[0] >= '0' && valueBuf[0] <= '9') || valueBuf[0] == '-' || valueBuf[0] == '.' {
|
||||
value, err = parseNumber(valueBuf)
|
||||
if err != nil {
|
||||
fmt.Printf("unable to parse number value '%v': %v\n", string(valueBuf), err)
|
||||
value = float64(0)
|
||||
panic(fmt.Sprintf("unable to parse number value '%v': %v", string(valueBuf), err))
|
||||
}
|
||||
|
||||
// Otherwise parse it as bool
|
||||
} else {
|
||||
value, err = strconv.ParseBool(string(valueBuf))
|
||||
if err != nil {
|
||||
fmt.Printf("unable to parse bool value '%v': %v\n", string(valueBuf), err)
|
||||
value = false
|
||||
panic(fmt.Sprintf("unable to parse bool value '%v': %v\n", string(valueBuf), err))
|
||||
}
|
||||
}
|
||||
fields[string(unescape(name))] = value
|
||||
|
|
|
@ -159,6 +159,108 @@ func TestParsePointMissingTagValue(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestParsePointMissingFieldValue(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=`)
|
||||
if err == nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, "cpu")
|
||||
}
|
||||
|
||||
_, err = ParsePointsString(`cpu,host=serverA,region=us-west value= 1000000000`)
|
||||
if err == nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, "cpu")
|
||||
}
|
||||
|
||||
_, err = ParsePointsString(`cpu,host=serverA,region=us-west value=,value2=1`)
|
||||
if err == nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, "cpu")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointBadNumber(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=1a`)
|
||||
if err == nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, `cpu,host=serverA,region=us-west value=1a`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointNumberNonNumeric(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=.1a`)
|
||||
if err == nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, `cpu,host=serverA,region=us-west value=.1a`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointNegativeWrongPlace(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=0.-1`)
|
||||
if err == nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, `cpu,host=serverA,region=us-west value=0.-1`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointFloatMultipleDecimals(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=1.1.1`)
|
||||
if err == nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, `cpu,host=serverA,region=us-west value=1.1.1`)
|
||||
}
|
||||
println(err.Error())
|
||||
}
|
||||
|
||||
func TestParsePointInteger(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=1`)
|
||||
if err != nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got %v, exp nil`, `cpu,host=serverA,region=us-west value=1`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointNegativeInteger(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=-1`)
|
||||
if err != nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got %v, exp nil`, `cpu,host=serverA,region=us-west value=-1`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointNegativeFloat(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=-1.0`)
|
||||
if err != nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got %v, exp nil`, `cpu,host=serverA,region=us-west value=-1.0`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointFloatNoLeadingDigit(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=.1`)
|
||||
if err != nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got %v, exp nil`, `cpu,host=serverA,region=us-west value=-1.0`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointFloatScientific(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=1.0e4`)
|
||||
if err != nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got %v, exp nil`, `cpu,host=serverA,region=us-west value=1.0e4`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointFloatScientificDecimal(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=1.0e-4`)
|
||||
if err != nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got %v, exp nil`, `cpu,host=serverA,region=us-west value=1.0e-4`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointFloatNegativeScientific(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=-1.0e-4`)
|
||||
if err != nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got %v, exp nil`, `cpu,host=serverA,region=us-west value=-1.0e-4`, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointBooleanInvalid(t *testing.T) {
|
||||
_, err := ParsePointsString(`cpu,host=serverA,region=us-west value=a`)
|
||||
if err == nil {
|
||||
t.Errorf(`ParsePoints("%s") mismatch. got nil, exp error`, `cpu,host=serverA,region=us-west value=a`)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParsePointUnescape(t *testing.T) {
|
||||
// commas in measuremnt name
|
||||
test(t, `cpu\,main,regions=east\,west value=1.0`,
|
||||
|
@ -384,7 +486,7 @@ func TestParsePointWithStringWithEquals(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestParsePointWithBoolField(t *testing.T) {
|
||||
test(t, `cpu,host=serverA,region=us-east bool=true,boolTrue=t,false=false,falseVal=f 1000000000`,
|
||||
test(t, `cpu,host=serverA,region=us-east true=true,t=t,T=T,TRUE=TRUE,false=false,f=f,F=F,FALSE=FALSE 1000000000`,
|
||||
NewPoint(
|
||||
"cpu",
|
||||
Tags{
|
||||
|
@ -392,10 +494,14 @@ func TestParsePointWithBoolField(t *testing.T) {
|
|||
"region": "us-east",
|
||||
},
|
||||
Fields{
|
||||
"bool": true,
|
||||
"boolTrue": true,
|
||||
"false": false,
|
||||
"falseVal": false,
|
||||
"t": true,
|
||||
"T": true,
|
||||
"true": true,
|
||||
"TRUE": true,
|
||||
"f": false,
|
||||
"F": false,
|
||||
"false": false,
|
||||
"FALSE": false,
|
||||
},
|
||||
time.Unix(1, 0)),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue