storage/db: read multi-line records from database

The database format allows multiple lines to be stored in a single
record, to reduce the number of rows in the database. This change
allows reading such records, though there is not yet any code which
can write them. This change also adds a test that will benefit from
this record compression.

Change-Id: Ia86153fb150534d701d01a0c102be5a3c29b826d
Reviewed-on: https://go-review.googlesource.com/35672
Reviewed-by: Russ Cox <rsc@golang.org>
diff --git a/storage/db/db.go b/storage/db/db.go
index a87be2e..decb63a 100644
--- a/storage/db/db.go
+++ b/storage/db/db.go
@@ -454,8 +454,8 @@
 type Query struct {
 	rows *sql.Rows
 	// from last call to Next
-	result *benchfmt.Result
-	err    error
+	br  *benchfmt.Reader
+	err error
 }
 
 // Next prepares the next result for reading with the Result
@@ -465,6 +465,15 @@
 	if q.err != nil {
 		return false
 	}
+	if q.br != nil {
+		if q.br.Next() {
+			return true
+		}
+		q.err = q.br.Err()
+		if q.err != nil {
+			return false
+		}
+	}
 	if !q.rows.Next() {
 		return false
 	}
@@ -473,22 +482,20 @@
 	if q.err != nil {
 		return false
 	}
-	// TODO(quentin): Needs to change when one row contains multiple Results.
-	br := benchfmt.NewReader(bytes.NewReader(content))
-	if !br.Next() {
-		q.err = br.Err()
+	q.br = benchfmt.NewReader(bytes.NewReader(content))
+	if !q.br.Next() {
+		q.err = q.br.Err()
 		if q.err == nil {
 			q.err = io.ErrUnexpectedEOF
 		}
 		return false
 	}
-	q.result = br.Result()
 	return q.err == nil
 }
 
 // Result returns the most recent result generated by a call to Next.
 func (q *Query) Result() *benchfmt.Result {
-	return q.result
+	return q.br.Result()
 }
 
 // Err returns the error state of the query.
diff --git a/storage/db/db_test.go b/storage/db/db_test.go
index e376cda..9b108eb 100644
--- a/storage/db/db_test.go
+++ b/storage/db/db_test.go
@@ -127,8 +127,11 @@
 	r.Labels["uploadid"] = u.ID
 	for _, num := range []string{"1", "2"} {
 		r.Labels["num"] = num
-		if err := u.InsertRecord(r); err != nil {
-			t.Fatalf("InsertRecord: %v", err)
+		for _, num2 := range []int{1, 2} {
+			r.Content = fmt.Sprintf("BenchmarkName %d ns/op", num2)
+			if err := u.InsertRecord(r); err != nil {
+				t.Fatalf("InsertRecord: %v", err)
+			}
 		}
 	}
 
@@ -141,11 +144,14 @@
 num: 1
 uploadid: 19700101.1
 BenchmarkName 1 ns/op
+BenchmarkName 2 ns/op
 num: 2
 BenchmarkName 1 ns/op
+BenchmarkName 2 ns/op
 `)
 
 	r.Labels["num"] = "3"
+	r.Content = "BenchmarkName 3 ns/op"
 
 	for _, uploadid := range []string{u.ID, "new"} {
 		u, err := db.ReplaceUpload(uploadid)
@@ -166,9 +172,9 @@
 		`key: value
 num: 3
 uploadid: 19700101.1
-BenchmarkName 1 ns/op
+BenchmarkName 3 ns/op
 uploadid: new
-BenchmarkName 1 ns/op
+BenchmarkName 3 ns/op
 `)
 }