synbase: syncQL: demo program enhancements
Respond to kash feedback on demo program:
- add column names
Note: the column names do not line up with the rows.
Max column widths aren't known until the entire
result set is iterated over.
Note2: as <name> would be useful to support since column names
are now being returned. I'll do that in a later CL.
- I checked in third_party github.com/peterh/liner for
libreadline functionality and use it here
(currently history is not persisted across executions)
- ctrl-d now exits (ctrl-c, quit and exit work too).
- make sure a newline is emitted on ctrl-c.
- change dump command to use the query language (select k, v from ...)
This prints out the values with %v and it is easy to see types
(and strings have quotes around them).
- add a missing closing paren in three error messages
- offset no longer printed in error messages.
This change does NOT do the following:
implement distinct(t)
Change-Id: I640280cd174c1735f5a431f62f86dba8c18e571b
diff --git a/v23/syncbase/nosql/internal/query/demo/db/db.go b/v23/syncbase/nosql/internal/query/demo/db/db.go
new file mode 100644
index 0000000..d59af00
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/demo/db/db.go
@@ -0,0 +1,177 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package db
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ "v.io/syncbase/v23/syncbase/nosql/internal/query/query_db"
+ "v.io/v23/vdl"
+)
+
+var d *demoDB
+
+func init() {
+ d = createDB()
+}
+
+type demoDB struct {
+ tables []table
+}
+
+type kv struct {
+ key string
+ value *vdl.Value
+}
+
+type table struct {
+ name string
+ rows []kv
+}
+
+type keyValueStreamImpl struct {
+ table table
+ cursor int
+ prefixes []string
+ prefixCursor int
+}
+
+func (kvs *keyValueStreamImpl) Advance() bool {
+ for true {
+ kvs.cursor++ // initialized to -1
+ if kvs.cursor >= len(kvs.table.rows) {
+ return false
+ }
+ for kvs.prefixCursor < len(kvs.prefixes) {
+ // does it match any prefix
+ if kvs.prefixes[kvs.prefixCursor] == "" || strings.HasPrefix(kvs.table.rows[kvs.cursor].key, kvs.prefixes[kvs.prefixCursor]) {
+ return true
+ }
+ // Keys and prefixes are both sorted low to high, so we can increment
+ // prefixCursor if the prefix is < the key.
+ if kvs.prefixes[kvs.prefixCursor] < kvs.table.rows[kvs.cursor].key {
+ kvs.prefixCursor++
+ if kvs.prefixCursor >= len(kvs.prefixes) {
+ return false
+ }
+ } else {
+ break
+ }
+ }
+ }
+ return false
+}
+
+func (kvs *keyValueStreamImpl) KeyValue() (string, *vdl.Value) {
+ return kvs.table.rows[kvs.cursor].key, kvs.table.rows[kvs.cursor].value
+}
+
+func (kvs *keyValueStreamImpl) Err() error {
+ return nil
+}
+
+func (kvs *keyValueStreamImpl) Cancel() {
+}
+
+func (t table) Scan(prefixes []string) (query_db.KeyValueStream, error) {
+ var keyValueStreamImpl keyValueStreamImpl
+ keyValueStreamImpl.table = t
+ keyValueStreamImpl.cursor = -1
+ keyValueStreamImpl.prefixes = prefixes
+ return &keyValueStreamImpl, nil
+}
+
+// GetTableNames is not part of the query_db.Database interface.
+// Tables are discovered outside the query package.
+// TODO(jkline): Consider system tables to discover info such as this.
+func GetTableNames() []string {
+ tables := []string{}
+ for _, table := range d.tables {
+ tables = append(tables, table.name)
+ }
+ return tables
+}
+
+// GetDatabase in not part of the query_db.Database interface.
+// Database instances are obtained outside the query package.
+func GetDatabase() query_db.Database {
+ return d
+}
+
+func (d demoDB) GetTable(table string) (query_db.Table, error) {
+ for _, t := range d.tables {
+ if t.name == table {
+ return t, nil
+ }
+ }
+ return nil, errors.New(fmt.Sprintf("No such table: %s.", table))
+}
+
+func createDB() *demoDB {
+ d = &demoDB{}
+ var custTable table
+ custTable.name = "Customer"
+ custTable.rows = []kv{
+ kv{
+ "001",
+ vdl.ValueOf(Customer{"John Smith", 1, true, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, CreditReport{Agency: CreditAgencyEquifax, Report: AgencyReportEquifaxReport{EquifaxCreditReport{'A'}}}}),
+ },
+ kv{
+ "001001",
+ vdl.ValueOf(Invoice{1, 1000, 42, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}}),
+ },
+ kv{
+ "001002",
+ vdl.ValueOf(Invoice{1, 1003, 7, AddressInfo{"2 Main St.", "Palo Alto", "CA", "94303"}}),
+ },
+ kv{
+ "001003",
+ vdl.ValueOf(Invoice{1, 1005, 88, AddressInfo{"3 Main St.", "Palo Alto", "CA", "94303"}}),
+ },
+ kv{
+ "002",
+ vdl.ValueOf(Customer{"Bat Masterson", 2, true, AddressInfo{"777 Any St.", "Collins", "IA", "50055"}, CreditReport{Agency: CreditAgencyTransUnion, Report: AgencyReportTransUnionReport{TransUnionCreditReport{80}}}}),
+ },
+ kv{
+ "002001",
+ vdl.ValueOf(Invoice{2, 1001, 166, AddressInfo{"777 Any St.", "collins", "IA", "50055"}}),
+ },
+ kv{
+ "002002",
+ vdl.ValueOf(Invoice{2, 1002, 243, AddressInfo{"888 Any St.", "collins", "IA", "50055"}}),
+ },
+ kv{
+ "002003",
+ vdl.ValueOf(Invoice{2, 1004, 787, AddressInfo{"999 Any St.", "collins", "IA", "50055"}}),
+ },
+ kv{
+ "002004",
+ vdl.ValueOf(Invoice{2, 1006, 88, AddressInfo{"101010 Any St.", "collins", "IA", "50055"}}),
+ },
+ }
+ d.tables = append(d.tables, custTable)
+
+ var numTable table
+ numTable.name = "Numbers"
+ numTable.rows = []kv{
+ kv{
+ "001",
+ vdl.ValueOf(Numbers{byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), int64(128), float32(3.14159), float64(2.71828182846), complex64(123.0 + 7.0i), complex128(456.789 + 10.1112i)}),
+ },
+ kv{
+ "002",
+ vdl.ValueOf(Numbers{byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), int64(88), float32(1.41421356237), float64(1.73205080757), complex64(9.87 + 7.65i), complex128(4.32 + 1.0i)}),
+ },
+ kv{
+ "003",
+ vdl.ValueOf(Numbers{byte(210), uint16(210), uint32(210), uint64(210), int16(210), int32(210), int64(210), float32(210.0), float64(210.0), complex64(210.0 + 0.0i), complex128(210.0 + 0.0i)}),
+ },
+ }
+ d.tables = append(d.tables, numTable)
+
+ return d
+}
diff --git a/v23/syncbase/nosql/internal/query/demo/db_objects.vdl b/v23/syncbase/nosql/internal/query/demo/db/db_objects.vdl
similarity index 98%
rename from v23/syncbase/nosql/internal/query/demo/db_objects.vdl
rename to v23/syncbase/nosql/internal/query/demo/db/db_objects.vdl
index a6338c0..e21e4f5 100644
--- a/v23/syncbase/nosql/internal/query/demo/db_objects.vdl
+++ b/v23/syncbase/nosql/internal/query/demo/db/db_objects.vdl
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main
+package db
type AddressInfo struct {
Street string
diff --git a/v23/syncbase/nosql/internal/query/demo/db_objects.vdl.go b/v23/syncbase/nosql/internal/query/demo/db/db_objects.vdl.go
similarity index 94%
rename from v23/syncbase/nosql/internal/query/demo/db_objects.vdl.go
rename to v23/syncbase/nosql/internal/query/demo/db/db_objects.vdl.go
index dd96a5c..72bab1a 100644
--- a/v23/syncbase/nosql/internal/query/demo/db_objects.vdl.go
+++ b/v23/syncbase/nosql/internal/query/demo/db/db_objects.vdl.go
@@ -5,7 +5,7 @@
// This file was auto-generated by the vanadium vdl tool.
// Source: db_objects.vdl
-package main
+package db
import (
// VDL system imports
@@ -21,7 +21,7 @@
}
func (AddressInfo) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.AddressInfo"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.AddressInfo"`
}) {
}
@@ -56,7 +56,7 @@
return nil
}
*x = -1
- return fmt.Errorf("unknown label %q in main.CreditAgency", label)
+ return fmt.Errorf("unknown label %q in db.CreditAgency", label)
}
// String returns the string label of x.
@@ -73,7 +73,7 @@
}
func (CreditAgency) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.CreditAgency"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.CreditAgency"`
Enum struct{ Equifax, Experian, TransUnion string }
}) {
}
@@ -105,7 +105,7 @@
return nil
}
*x = -1
- return fmt.Errorf("unknown label %q in main.ExperianRating", label)
+ return fmt.Errorf("unknown label %q in db.ExperianRating", label)
}
// String returns the string label of x.
@@ -120,7 +120,7 @@
}
func (ExperianRating) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.ExperianRating"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.ExperianRating"`
Enum struct{ Good, Bad string }
}) {
}
@@ -130,7 +130,7 @@
}
func (EquifaxCreditReport) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.EquifaxCreditReport"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.EquifaxCreditReport"`
}) {
}
@@ -139,7 +139,7 @@
}
func (ExperianCreditReport) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.ExperianCreditReport"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.ExperianCreditReport"`
}) {
}
@@ -148,7 +148,7 @@
}
func (TransUnionCreditReport) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.TransUnionCreditReport"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.TransUnionCreditReport"`
}) {
}
@@ -172,7 +172,7 @@
AgencyReportTransUnionReport struct{ Value TransUnionCreditReport }
// __AgencyReportReflect describes the AgencyReport union type.
__AgencyReportReflect struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.AgencyReport"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.AgencyReport"`
Type AgencyReport
Union struct {
EquifaxReport AgencyReportEquifaxReport
@@ -203,7 +203,7 @@
}
func (CreditReport) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.CreditReport"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.CreditReport"`
}) {
}
@@ -216,7 +216,7 @@
}
func (Customer) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.Customer"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.Customer"`
}) {
}
@@ -228,7 +228,7 @@
}
func (Invoice) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.Invoice"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.Invoice"`
}) {
}
@@ -247,7 +247,7 @@
}
func (Numbers) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.Numbers"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.Numbers"`
}) {
}
@@ -256,7 +256,7 @@
}
func (FooType) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.FooType"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.FooType"`
}) {
}
@@ -265,7 +265,7 @@
}
func (BarType) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.BarType"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.BarType"`
}) {
}
@@ -287,7 +287,7 @@
TitleOrValueTypeValue struct{ Value int64 }
// __TitleOrValueTypeReflect describes the TitleOrValueType union type.
__TitleOrValueTypeReflect struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.TitleOrValueType"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.TitleOrValueType"`
Type TitleOrValueType
Union struct {
Title TitleOrValueTypeTitle
@@ -312,7 +312,7 @@
}
func (BazType) __VDLReflect(struct {
- Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo.BazType"`
+ Name string `vdl:"v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db.BazType"`
}) {
}
diff --git a/v23/syncbase/nosql/internal/query/demo/db/doc.go b/v23/syncbase/nosql/internal/query/demo/db/doc.go
new file mode 100644
index 0000000..e6a1508
--- /dev/null
+++ b/v23/syncbase/nosql/internal/query/demo/db/doc.go
@@ -0,0 +1,6 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// An in-memory implementation of query_db.
+package db
diff --git a/v23/syncbase/nosql/internal/query/demo/demo.go b/v23/syncbase/nosql/internal/query/demo/demo.go
index 81274e0..e5c5e47 100644
--- a/v23/syncbase/nosql/internal/query/demo/demo.go
+++ b/v23/syncbase/nosql/internal/query/demo/demo.go
@@ -5,186 +5,41 @@
package main
import (
- "bufio"
- "errors"
"fmt"
+ "io"
"os"
"strings"
+ "github.com/peterh/liner"
"v.io/syncbase/v23/syncbase/nosql/internal/query"
+ "v.io/syncbase/v23/syncbase/nosql/internal/query/demo/db"
"v.io/syncbase/v23/syncbase/nosql/internal/query/query_db"
- "v.io/v23/vdl"
)
-type demoDB struct {
- tables []table
-}
-
-type kv struct {
- key string
- value *vdl.Value
-}
-
-type table struct {
- name string
- rows []kv
-}
-
-type keyValueStreamImpl struct {
- table table
- cursor int
- prefixes []string
- prefixCursor int
-}
-
-func (kvs *keyValueStreamImpl) Advance() bool {
- for true {
- kvs.cursor++ // initialized to -1
- if kvs.cursor >= len(kvs.table.rows) {
- return false
- }
- for kvs.prefixCursor < len(kvs.prefixes) {
- // does it match any prefix
- if kvs.prefixes[kvs.prefixCursor] == "" || strings.HasPrefix(kvs.table.rows[kvs.cursor].key, kvs.prefixes[kvs.prefixCursor]) {
- return true
- }
- // Keys and prefixes are both sorted low to high, so we can increment
- // prefixCursor if the prefix is < the key.
- if kvs.prefixes[kvs.prefixCursor] < kvs.table.rows[kvs.cursor].key {
- kvs.prefixCursor++
- if kvs.prefixCursor >= len(kvs.prefixes) {
- return false
- }
- } else {
- break
- }
- }
- }
- return false
-}
-
-func (kvs *keyValueStreamImpl) KeyValue() (string, *vdl.Value) {
- return kvs.table.rows[kvs.cursor].key, kvs.table.rows[kvs.cursor].value
-}
-
-func (kvs *keyValueStreamImpl) Err() error {
- return nil
-}
-
-func (kvs *keyValueStreamImpl) Cancel() {
-}
-
-func (t table) Scan(prefixes []string) (query_db.KeyValueStream, error) {
- var keyValueStreamImpl keyValueStreamImpl
- keyValueStreamImpl.table = t
- keyValueStreamImpl.cursor = -1
- keyValueStreamImpl.prefixes = prefixes
- return &keyValueStreamImpl, nil
-}
-
-func (db demoDB) GetTable(table string) (query_db.Table, error) {
- for _, t := range db.tables {
- if t.name == table {
- return t, nil
- }
- }
- return nil, errors.New(fmt.Sprintf("No such table: %s.", table))
-
-}
-
-func createDB() query_db.Database {
- var db demoDB
- var custTable table
- custTable.name = "Customer"
- custTable.rows = []kv{
- kv{
- "001",
- vdl.ValueOf(Customer{"John Smith", 1, true, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}, CreditReport{Agency: CreditAgencyEquifax, Report: AgencyReportEquifaxReport{EquifaxCreditReport{'A'}}}}),
- },
- kv{
- "001001",
- vdl.ValueOf(Invoice{1, 1000, 42, AddressInfo{"1 Main St.", "Palo Alto", "CA", "94303"}}),
- },
- kv{
- "001002",
- vdl.ValueOf(Invoice{1, 1003, 7, AddressInfo{"2 Main St.", "Palo Alto", "CA", "94303"}}),
- },
- kv{
- "001003",
- vdl.ValueOf(Invoice{1, 1005, 88, AddressInfo{"3 Main St.", "Palo Alto", "CA", "94303"}}),
- },
- kv{
- "002",
- vdl.ValueOf(Customer{"Bat Masterson", 2, true, AddressInfo{"777 Any St.", "Collins", "IA", "50055"}, CreditReport{Agency: CreditAgencyTransUnion, Report: AgencyReportTransUnionReport{TransUnionCreditReport{80}}}}),
- },
- kv{
- "002001",
- vdl.ValueOf(Invoice{2, 1001, 166, AddressInfo{"777 Any St.", "collins", "IA", "50055"}}),
- },
- kv{
- "002002",
- vdl.ValueOf(Invoice{2, 1002, 243, AddressInfo{"888 Any St.", "collins", "IA", "50055"}}),
- },
- kv{
- "002003",
- vdl.ValueOf(Invoice{2, 1004, 787, AddressInfo{"999 Any St.", "collins", "IA", "50055"}}),
- },
- kv{
- "002004",
- vdl.ValueOf(Invoice{2, 1006, 88, AddressInfo{"101010 Any St.", "collins", "IA", "50055"}}),
- },
- }
- db.tables = append(db.tables, custTable)
-
- var numTable table
- numTable.name = "Numbers"
- numTable.rows = []kv{
- kv{
- "001",
- vdl.ValueOf(Numbers{byte(12), uint16(1234), uint32(5678), uint64(999888777666), int16(9876), int32(876543), int64(128), float32(3.14159), float64(2.71828182846), complex64(123.0 + 7.0i), complex128(456.789 + 10.1112i)}),
- },
- kv{
- "002",
- vdl.ValueOf(Numbers{byte(9), uint16(99), uint32(999), uint64(9999999), int16(9), int32(99), int64(88), float32(1.41421356237), float64(1.73205080757), complex64(9.87 + 7.65i), complex128(4.32 + 1.0i)}),
- },
- kv{
- "003",
- vdl.ValueOf(Numbers{byte(210), uint16(210), uint32(210), uint64(210), int16(210), int32(210), int64(210), float32(210.0), float64(210.0), complex64(210.0 + 0.0i), complex128(210.0 + 0.0i)}),
- },
- }
- db.tables = append(db.tables, numTable)
-
- return db
-}
-
-func dumpDB(db query_db.Database) {
- switch db := db.(type) {
- case demoDB:
- for _, t := range db.tables {
- fmt.Printf("table: %s\n", t.name)
- for _, row := range t.rows {
- fmt.Printf("key: %s, value: %v", row.key, row.value)
- }
- }
+func dumpDB(d query_db.Database) {
+ for _, table := range db.GetTableNames() {
+ fmt.Printf("table: %s\n", table)
+ queryExec(d, fmt.Sprintf("select k, v from %s", table))
}
}
-func dumpAddress(a *AddressInfo) string {
- return fmt.Sprintf("Street:%v,City:%v,State:%v,Zip:%v", a.Street, a.City, a.State, a.Zip)
-}
-
-func queryExec(db query_db.Database, q string) {
- if rs, err := query.Exec(db, q); err != nil {
+func queryExec(d query_db.Database, q string) {
+ if columnNames, rs, err := query.Exec(d, q); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", q)
fmt.Fprintf(os.Stderr, "%s^\n", strings.Repeat(" ", int(err.Off)))
- fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
+ fmt.Fprintf(os.Stderr, "Error: %s\n", err.Msg)
} else {
+ sep := ""
+ for _, cName := range columnNames {
+ fmt.Printf("%s%s", sep, cName)
+ sep = " | "
+ }
+ fmt.Printf("\n")
for rs.Advance() {
- for i, column := range rs.Result() {
- if i != 0 {
- fmt.Printf(",")
- }
- fmt.Printf("%v", column)
+ sep = ""
+ for _, column := range rs.Result() {
+ fmt.Printf("%s%v", sep, column)
+ sep = " | "
}
fmt.Printf("\n")
}
@@ -195,22 +50,30 @@
}
func main() {
- db := createDB()
- reader := bufio.NewReader(os.Stdin)
+ line := liner.NewLiner()
+ defer line.Close()
+ line.SetCtrlCAborts(true)
+
+ d := db.GetDatabase()
for true {
- fmt.Printf("Enter query (or 'dump' or 'exit')? ")
- q, err := reader.ReadString('\n')
- if err != nil {
- fmt.Fprintf(os.Stderr, fmt.Sprintf("Input error: %s\n", err.Error()))
- } else {
- // kill the newline
- q = q[0 : len(q)-1]
- if q == "" || strings.ToLower(q) == "exit" {
- os.Exit(0)
- } else if strings.ToLower(q) == "dump" {
- dumpDB(db)
+ if q, err := line.Prompt("Enter query or 'dump'? "); err != nil {
+ if err == io.EOF {
+ // ctrl-d
+ fmt.Println()
+ break
} else {
- queryExec(db, q)
+ // ctrl-c
+ break
+ }
+ } else {
+ if q == "" || strings.ToLower(q) == "exit" || strings.ToLower(q) == "quit" {
+ break
+ }
+ line.AppendHistory(q)
+ if strings.ToLower(q) == "dump" {
+ dumpDB(d)
+ } else {
+ queryExec(d, q)
}
}
}
diff --git a/v23/syncbase/nosql/internal/query/query.go b/v23/syncbase/nosql/internal/query/query.go
index 5c76cd2..c0a17ea 100644
--- a/v23/syncbase/nosql/internal/query/query.go
+++ b/v23/syncbase/nosql/internal/query/query.go
@@ -26,19 +26,19 @@
Cancel()
}
-func Exec(db query_db.Database, q string) (ResultStream, *QueryError) {
+func Exec(db query_db.Database, q string) ([]string, ResultStream, *QueryError) {
s, err := query_parser.Parse(q)
if err != nil {
- return nil, ErrorFromSyntax(err)
+ return nil, nil, ErrorFromSyntax(err)
}
if err := query_checker.Check(db, s); err != nil {
- return nil, ErrorFromSemantic(err)
+ return nil, nil, ErrorFromSemantic(err)
}
switch sel := (*s).(type) {
case query_parser.SelectStatement:
return execSelect(db, &sel)
default:
- return nil, Error((*s).Offset(), fmt.Sprintf("Cannot exec statement type %v", reflect.TypeOf(*s)))
+ return nil, nil, Error((*s).Offset(), fmt.Sprintf("Cannot exec statement type %v", reflect.TypeOf(*s)))
}
}
@@ -156,14 +156,28 @@
rs.keyValueStream.Cancel()
}
-func execSelect(db query_db.Database, s *query_parser.SelectStatement) (ResultStream, *QueryError) {
+func getColumnHeadings(s *query_parser.SelectStatement) []string {
+ columnHeaders := []string{}
+ for _, field := range s.Select.Columns {
+ sep := ""
+ columnName := ""
+ for _, segment := range field.Segments {
+ columnName = columnName + sep + segment.Value
+ sep = "."
+ }
+ columnHeaders = append(columnHeaders, columnName)
+ }
+ return columnHeaders
+}
+
+func execSelect(db query_db.Database, s *query_parser.SelectStatement) ([]string, ResultStream, *QueryError) {
prefixes := CompileKeyPrefixes(s.Where)
keyValueStream, err := s.From.Table.DBTable.Scan(prefixes)
if err != nil {
- return nil, Error(s.Off, err.Error())
+ return nil, nil, Error(s.Off, err.Error())
}
var resultStream resultStreamImpl
resultStream.selectStatement = s
resultStream.keyValueStream = keyValueStream
- return &resultStream, nil
+ return getColumnHeadings(s), &resultStream, nil
}
diff --git a/v23/syncbase/nosql/internal/query/query_parser/query_parser.go b/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
index 2e45daf..66964c5 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/query_parser.go
@@ -684,7 +684,7 @@
}
token = scanToken(s)
default:
- return nil, nil, Error(token.Off, fmt.Sprintf("Expected operator ('like', 'not like', '=', '<>', '<', '<=', '>', '>=', 'equal' or 'not equal', found '%s'.", token.Value))
+ return nil, nil, Error(token.Off, fmt.Sprintf("Expected operator ('like', 'not like', '=', '<>', '<', '<=', '>', '>=', 'equal' or 'not equal'), found '%s'.", token.Value))
}
} else {
switch token.Tok {
@@ -715,7 +715,7 @@
operator.Type = GreaterThan
}
default:
- return nil, nil, Error(token.Off, fmt.Sprintf("Expected operator ('like', 'not like', '=', '<>', 'equal' or 'not equal', found '%s'.", token.Value))
+ return nil, nil, Error(token.Off, fmt.Sprintf("Expected operator ('like', 'not like', '=', '<>', 'equal' or 'not equal'), found '%s'.", token.Value))
}
}
@@ -735,7 +735,7 @@
case "or":
operator.Type = Or
default:
- return nil, nil, Error(token.Off, fmt.Sprintf("Expected operator ('and' or 'or', found '%s'.", token.Value))
+ return nil, nil, Error(token.Off, fmt.Sprintf("Expected operator ('and' or 'or'), found '%s'.", token.Value))
}
token = scanToken(s)
diff --git a/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go b/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
index 8053e43..ee19add 100644
--- a/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
+++ b/v23/syncbase/nosql/internal/query/query_parser/query_parser_test.go
@@ -2211,7 +2211,7 @@
{"select foo from Customer Invoice", query_parser.Error(25, "Unexpected: 'Invoice'.")},
{"select (foo) from (Customer)", query_parser.Error(7, "Expected identifier, found '('.")},
{"select foo, bar from Customer where a = (b)", query_parser.Error(40, "Expected operand, found '('.")},
- {"select foo, bar from Customer where a = b and (c) = d", query_parser.Error(48, "Expected operator ('like', 'not like', '=', '<>', 'equal' or 'not equal', found ')'.")},
+ {"select foo, bar from Customer where a = b and (c) = d", query_parser.Error(48, "Expected operator ('like', 'not like', '=', '<>', 'equal' or 'not equal'), found ')'.")},
{"select foo, bar from Customer where a = b and c =", query_parser.Error(49, "Unexpected end of statement, expected operand.")},
{"select foo, bar from Customer where a = ", query_parser.Error(40, "Unexpected end of statement, expected operand.")},
{"select foo, bar from Customer where a", query_parser.Error(37, "Unexpected end of statement, expected operator.")},
@@ -2219,11 +2219,11 @@
{"select a from", query_parser.Error(13, "Unexpected end of statement.")},
{"select a from b where c = d and e =", query_parser.Error(35, "Unexpected end of statement, expected operand.")},
{"select a from b where c = d and f", query_parser.Error(33, "Unexpected end of statement, expected operator.")},
- {"select a from b where c = d and f *", query_parser.Error(34, "Expected operator ('like', 'not like', '=', '<>', 'equal' or 'not equal', found '*'.")},
+ {"select a from b where c = d and f *", query_parser.Error(34, "Expected operator ('like', 'not like', '=', '<>', 'equal' or 'not equal'), found '*'.")},
{"select a from b where c <", query_parser.Error(25, "Unexpected end of statement, expected operand.")},
{"select a from b where c not", query_parser.Error(27, "Expected 'equal' or 'like'")},
{"select a from b where c not 8", query_parser.Error(28, "Expected 'equal' or 'like'")},
- {"select x from y where a and b = c", query_parser.Error(24, "Expected operator ('like', 'not like', '=', '<>', '<', '<=', '>', '>=', 'equal' or 'not equal', found 'and'.")},
+ {"select x from y where a and b = c", query_parser.Error(24, "Expected operator ('like', 'not like', '=', '<>', '<', '<=', '>', '>=', 'equal' or 'not equal'), found 'and'.")},
{"select v from Customer limit 100 offset a", query_parser.Error(40, "Expected positive integer literal., found 'a'.")},
{"select v from Customer limit -100 offset 5", query_parser.Error(29, "Expected positive integer literal., found '-'.")},
{"select v from Customer limit 100 offset -5", query_parser.Error(40, "Expected positive integer literal., found '-'.")},
diff --git a/v23/syncbase/nosql/internal/query/test/query_test.go b/v23/syncbase/nosql/internal/query/test/query_test.go
index f61b8b1..6c597eb 100644
--- a/v23/syncbase/nosql/internal/query/test/query_test.go
+++ b/v23/syncbase/nosql/internal/query/test/query_test.go
@@ -589,7 +589,7 @@
}
for _, test := range basic {
- rs, err := query.Exec(db, test.query)
+ _, rs, err := query.Exec(db, test.query)
if err != nil {
t.Errorf("query: %s; got %v, want nil", test.query, err)
} else {
@@ -1191,7 +1191,7 @@
}
for _, test := range basic {
- _, err := query.Exec(db, test.query)
+ _, _, err := query.Exec(db, test.query)
if !reflect.DeepEqual(err, test.err) {
t.Errorf("query: %s; got %v, want %v", test.query, err, test.err)
}