Commit 74056580 authored by Quentin Smith's avatar Quentin Smith

storage/db: implement range queries

Change-Id: I954c533190353a16be9cbe55541fef7b0aaa818b
Reviewed-on: https://go-review.googlesource.com/35877Reviewed-by: default avatarRuss Cox <rsc@golang.org>
parent f4f7b115
......@@ -9,7 +9,6 @@ package db
import (
"bytes"
"database/sql"
"errors"
"fmt"
"io"
"regexp"
......@@ -369,37 +368,44 @@ func (db *DB) Query(q string) *Query {
qparts := splitQueryWords(q)
var args []interface{}
Words:
for _, part := range qparts {
for i, c := range part {
switch {
case c == ':':
args = append(args, part[:i], part[i+1:])
continue Words
case c == '>' || c == '<':
// TODO
return &Query{err: errors.New("unsupported operator")}
case unicode.IsSpace(c) || unicode.IsUpper(c):
return &Query{err: fmt.Errorf("query part %q has invalid key", part)}
}
}
return &Query{err: fmt.Errorf("query part %q is missing operator", part)}
}
query := "SELECT r.Content FROM "
for i := 0; i < len(args)/2; i++ {
for i, part := range qparts {
if i > 0 {
query += " INNER JOIN "
}
query += fmt.Sprintf("(SELECT UploadID, RecordID FROM RecordLabels WHERE Name = ? AND Value = ?) t%d", i)
sepIndex := strings.IndexFunc(part, func(r rune) bool {
return r == ':' || r == '>' || r == '<' || unicode.IsSpace(r) || unicode.IsUpper(r)
})
if sepIndex < 0 {
return &Query{err: fmt.Errorf("query part %q is missing operator", part)}
}
key, sep, value := part[:sepIndex], part[sepIndex], part[sepIndex+1:]
switch sep {
case ':':
if value == "" {
// TODO(quentin): Implement support for searching for missing labels.
return &Query{err: fmt.Errorf("missing value for query part %q", part)}
}
query += fmt.Sprintf("(SELECT UploadID, RecordID FROM RecordLabels WHERE Name = ? AND Value = ?) t%d", i)
args = append(args, key, value)
case '>', '<':
query += fmt.Sprintf("(SELECT UploadID, RecordID FROM RecordLabels WHERE Name = ? AND Value %c ?) t%d", sep, i)
args = append(args, key, value)
default:
return &Query{err: fmt.Errorf("query part %q has invalid key", part)}
}
if i > 0 {
query += " USING (UploadID, RecordID)"
}
}
// TODO(quentin): Handle empty query string.
query += " LEFT JOIN Records r USING (UploadID, RecordID)"
if len(qparts) > 0 {
query += " LEFT JOIN"
}
query += " Records r"
if len(qparts) > 0 {
query += " USING (UploadID, RecordID)"
}
rows, err := db.sql.Query(query, args...)
if err != nil {
......
......@@ -263,7 +263,10 @@ func TestQuery(t *testing.T) {
t.Fatalf("NewUpload: %v", err)
}
var allRecords []int
for i := 0; i < 1024; i++ {
allRecords = append(allRecords, i)
r := &benchfmt.Result{Labels: make(map[string]string), NameLabels: make(map[string]string), Content: "BenchmarkName 1 ns/op"}
for j := uint(0); j < 10; j++ {
r.Labels[fmt.Sprintf("label%d", j)] = fmt.Sprintf("%d", i/(1<<j))
......@@ -286,6 +289,9 @@ func TestQuery(t *testing.T) {
{"label0:5 name:Name", []int{5}},
{"label0:0 label0:5", []int{}},
{"bogus query", nil},
{"label1<2 label3:0", []int{0, 1, 2, 3}},
{"label1>510", []int{1022, 1023}},
{"", allRecords},
}
for _, test := range tests {
t.Run("query="+test.q, func(t *testing.T) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment