From 39f3480f28c47c0a855bc5f7043af4c705f679e9 Mon Sep 17 00:00:00 2001 From: Edd Robinson Date: Thu, 19 May 2016 16:58:18 +0100 Subject: [PATCH] Ensure points with trailing whitespace are accepted --- CHANGELOG.md | 1 + models/points.go | 43 +++++++++++++++--------- models/points_test.go | 77 ++++++++++++++++++++++++++++++------------- 3 files changed, 83 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 387e708065c..1e0757aa228 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ - [#6701](https://github.com/influxdata/influxdb/issues/6701): Filter out sources that do not match the shard database/retention policy. - [#6683](https://github.com/influxdata/influxdb/issues/6683): Fix compaction planning re-compacting large TSM files - [#6693](https://github.com/influxdata/influxdb/pull/6693): Truncate the shard group end time if it exceeds MaxNanoTime. +- [#6672](https://github.com/influxdata/influxdb/issues/6672): Accept points with trailing whitespace. ## v0.13.0 [2016-05-12] diff --git a/models/points.go b/models/points.go index 055f7e2c035..f4db4c67e20 100644 --- a/models/points.go +++ b/models/points.go @@ -29,6 +29,7 @@ var ( ErrPointMustHaveAField = errors.New("point without fields is unsupported") ErrInvalidNumber = errors.New("invalid number") + ErrInvalidPoint = errors.New("point is invalid") ErrMaxKeyLengthExceeded = errors.New("max key length exceeded") ) @@ -234,7 +235,6 @@ func parsePoint(buf []byte, defaultTime time.Time, precision string) (Point, err // scan the last block which is an optional integer timestamp pos, ts, err := scanTime(buf, pos) - if err != nil { return nil, err } @@ -257,6 +257,15 @@ func parsePoint(buf []byte, defaultTime time.Time, precision string) (Point, err if err != nil { return nil, err } + + // Determine if there are illegal non-whitespace characters after the + // timestamp block. + for pos < len(buf) { + if buf[pos] != ' ' { + return nil, ErrInvalidPoint + } + pos++ + } } return pt, nil } @@ -634,32 +643,34 @@ func scanFields(buf []byte, i int) (int, []byte, error) { return i, buf[start:i], nil } -// scanTime scans buf, starting at i for the time section of a point. It returns -// the ending position and the byte slice of the fields within buf and error if the -// timestamp is not in the correct numeric format +// scanTime scans buf, starting at i for the time section of a point. It +// returns the ending position and the byte slice of the timestamp within buf +// and and error if the timestamp is not in the correct numeric format. func scanTime(buf []byte, i int) (int, []byte, error) { start := skipWhitespace(buf, i) i = start + for { // reached the end of buf? if i >= len(buf) { break } - // Timestamps should be integers, make sure they are so we don't need to actually - // parse the timestamp until needed - if buf[i] < '0' || buf[i] > '9' { - // Handle negative timestamps - if i == start && buf[i] == '-' { - i++ - continue - } - return i, buf[start:i], fmt.Errorf("bad timestamp") + // Reached end of block or trailing whitespace? + if buf[i] == '\n' || buf[i] == ' ' { + break } - // reached end of block? - if buf[i] == '\n' { - break + // Handle negative timestamps + if i == start && buf[i] == '-' { + i++ + continue + } + + // Timestamps should be integers, make sure they are so we don't need + // to actually parse the timestamp until needed. + if buf[i] < '0' || buf[i] > '9' { + return i, buf[start:i], fmt.Errorf("bad timestamp") } i++ } diff --git a/models/points_test.go b/models/points_test.go index 50548df3d02..cc9d34c19de 100644 --- a/models/points_test.go +++ b/models/points_test.go @@ -586,6 +586,47 @@ func TestParsePointScientificIntInvalid(t *testing.T) { } } +func TestParsePointWhitespace(t *testing.T) { + examples := []string{ + `cpu value=1.0 1257894000000000000`, + `cpu value=1.0 1257894000000000000`, + `cpu value=1.0 1257894000000000000`, + `cpu value=1.0 1257894000000000000 `, + `cpu value=1.0 1257894000000000000 +`, + `cpu value=1.0 1257894000000000000 +`, + } + + expPoint := NewTestPoint("cpu", models.Tags{}, models.Fields{"value": 1.0}, time.Unix(0, 1257894000000000000)) + for i, example := range examples { + pts, err := models.ParsePoints([]byte(example)) + if err != nil { + t.Fatalf(`[Example %d] ParsePoints("%s") error. got %v, exp nil`, i, example, err) + } + + if got, exp := len(pts), 1; got != exp { + t.Fatalf("[Example %d] got %d points, expected %d", i, got, exp) + } + + if got, exp := pts[0].Name(), expPoint.Name(); got != exp { + t.Fatalf("[Example %d] got %d measurement, expected %d", i, got, exp) + } + + if got, exp := len(pts[0].Fields()), len(expPoint.Fields()); got != exp { + t.Fatalf("[Example %d] got %d fields, expected %d", i, got, exp) + } + + if got, exp := pts[0].Fields()["value"], expPoint.Fields()["value"]; got != exp { + t.Fatalf(`[Example %d] got %v for field "value", expected %v`, i, got, exp) + } + + if got, exp := pts[0].Time().UnixNano(), expPoint.Time().UnixNano(); got != exp { + t.Fatalf(`[Example %d] got %d time, expected %d`, i, got, exp) + } + } +} + func TestParsePointUnescape(t *testing.T) { // commas in measurement name test(t, `foo\,bar value=1i`, @@ -1171,29 +1212,21 @@ func TestParsePointMinTimestamp(t *testing.T) { } func TestParsePointInvalidTimestamp(t *testing.T) { - _, err := models.ParsePointsString("cpu value=1 9223372036854775808") - if err == nil { - t.Fatalf("ParsePoints failed: %v", err) - } - _, err = models.ParsePointsString("cpu value=1 -92233720368547758078") - if err == nil { - t.Fatalf("ParsePoints failed: %v", err) - } - _, err = models.ParsePointsString("cpu value=1 -") - if err == nil { - t.Fatalf("ParsePoints failed: %v", err) - } - _, err = models.ParsePointsString("cpu value=1 -/") - if err == nil { - t.Fatalf("ParsePoints failed: %v", err) - } - _, err = models.ParsePointsString("cpu value=1 -1?") - if err == nil { - t.Fatalf("ParsePoints failed: %v", err) + examples := []string{ + "cpu value=1 9223372036854775808", + "cpu value=1 -92233720368547758078", + "cpu value=1 -", + "cpu value=1 -/", + "cpu value=1 -1?", + "cpu value=1 1-", + "cpu value=1 9223372036854775807 12", } - _, err = models.ParsePointsString("cpu value=1 1-") - if err == nil { - t.Fatalf("ParsePoints failed: %v", err) + + for i, example := range examples { + _, err := models.ParsePointsString(example) + if err == nil { + t.Fatalf("[Example %d] ParsePoints failed: %v", i, err) + } } }