veyron/services/store/memstore/query: Handle QueryResult.Fields when present.
If a query uses selection, subsequent pipelines must refer to the
fields produced by that selection.
Added more todos for things that I need to implement and test.
They are dependent on sorting for deterministic tests, so I
can't do them now.
Change-Id: I0f30a1a5edc6bbe342c1fb0d1c41a511b1f3b000
diff --git a/services/store/memstore/query/eval.go b/services/store/memstore/query/eval.go
index 7061a67..bfee9ee 100644
--- a/services/store/memstore/query/eval.go
+++ b/services/store/memstore/query/eval.go
@@ -6,6 +6,8 @@
"path"
"reflect"
"runtime"
+ "sort"
+ "strings"
"sync"
"veyron/services/store/memstore/state"
@@ -448,6 +450,7 @@
pos: p.Pos,
}
for i, a := range p.SubPipelines {
+ // TODO(kash): Protect against aliases that have slashes in them?
e.subpipelines[i] = alias{convertPipeline(a.Pipeline), a.Alias, a.Hidden}
}
return e
@@ -811,12 +814,31 @@
// value implements the expr method.
func (e *exprName) value(c *context, result *store.QueryResult) interface{} {
- // TODO(kash): The name might be in result.Fields. Check there first.
+ if result.Fields != nil {
+ // TODO(kash): Handle multipart names. This currently only works if
+ // e.name has no slashes.
+ val, ok := result.Fields[e.name]
+ if !ok {
+ sendError(c.errc, fmt.Errorf("name '%s' was not selected from '%s', found: [%s]",
+ e.name, result.Name, mapKeys(result.Fields)))
+ return nil
+ }
+ return val
+ }
fullpath := path.Join(result.Name, e.name)
entry, err := c.sn.Get(c.clientID, storage.ParsePath(fullpath))
if err != nil {
- c.errc <- fmt.Errorf("could not look up name '%s' relative to '%s': %v", e.name, result.Name, err)
+ sendError(c.errc, fmt.Errorf("could not look up name '%s' relative to '%s': %v", e.name, result.Name, err))
return nil
}
return entry.Value
}
+
+func mapKeys(m map[string]idl.AnyData) string {
+ s := make([]string, 0, len(m))
+ for key, _ := range m {
+ s = append(s, key)
+ }
+ sort.Strings(s)
+ return strings.Join(s, ", ")
+}
diff --git a/services/store/memstore/query/eval_test.go b/services/store/memstore/query/eval_test.go
index 9a9d08c..4f8202f 100644
--- a/services/store/memstore/query/eval_test.go
+++ b/services/store/memstore/query/eval_test.go
@@ -175,6 +175,19 @@
nil},
},
},
+ {
+ "'teams/cardinals' | {Name as myname, Location as myloc} | ? myname == 'cardinals'",
+ []*store.QueryResult{
+ &store.QueryResult{
+ 0,
+ "teams/cardinals",
+ map[string]idl.AnyData{
+ "myname": "cardinals",
+ "myloc": "CA",
+ },
+ nil},
+ },
+ },
}
for _, test := range tests {
it := Eval(st.Snapshot(), rootPublicID, query.Query{test.query})
@@ -215,6 +228,8 @@
// TODO(kash): We probably want an error message that says that you must
// use a type filter.
{"teams/* | ?Name > 'foo'", "could not look up name 'Name' relative to 'teams': not found"},
+ {"'teams/cardinals' | {Name as myname, Location as myloc} | ? Name == 'foo'", "name 'Name' was not selected from 'teams/cardinals', found: [myloc, myname]"},
+
// TODO(kash): Selection with conflicting names.
}
for _, test := range tests {