x/ref/services/syncbase: Update Glob implementation
This change update the Glob implementation of syncbase to match the new
API.
Change-Id: I962d0420b658df8565d066f121567e9fc003adc0
diff --git a/services/syncbase/server/app.go b/services/syncbase/server/app.go
index 957f4ed..d7103e9 100644
--- a/services/syncbase/server/app.go
+++ b/services/syncbase/server/app.go
@@ -15,6 +15,7 @@
"v.io/syncbase/x/ref/services/syncbase/server/util"
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/verror"
@@ -82,17 +83,17 @@
return data.Perms, util.FormatVersion(data.Version), nil
}
-func (a *app) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
+func (a *app) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, matcher *glob.Element) error {
if !a.exists {
- return nil, verror.New(verror.ErrNoExist, ctx, a.name)
+ return verror.New(verror.ErrNoExist, ctx, a.name)
}
// Check perms.
sn := a.s.st.NewSnapshot()
if err := util.GetWithAuth(ctx, call, sn, a.stKey(), &appData{}); err != nil {
sn.Abort()
- return nil, err
+ return err
}
- return util.Glob(ctx, call, "*", sn, sn.Abort, util.JoinKeyParts(util.DbInfoPrefix, a.name))
+ return util.Glob(ctx, call, matcher, sn, sn.Abort, util.JoinKeyParts(util.DbInfoPrefix, a.name))
}
////////////////////////////////////////
diff --git a/services/syncbase/server/nosql/database.go b/services/syncbase/server/nosql/database.go
index d4875c0..7b9a470 100644
--- a/services/syncbase/server/nosql/database.go
+++ b/services/syncbase/server/nosql/database.go
@@ -22,6 +22,7 @@
"v.io/syncbase/x/ref/services/syncbase/server/watchable"
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/services/watch"
@@ -339,20 +340,20 @@
return nil, verror.NewErrNotImplemented(ctx)
}
-func (d *databaseReq) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
+func (d *databaseReq) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, matcher *glob.Element) error {
if !d.exists {
- return nil, verror.New(verror.ErrNoExist, ctx, d.name)
+ return verror.New(verror.ErrNoExist, ctx, d.name)
}
if d.batchId != nil {
- return nil, wire.NewErrBoundToBatch(ctx)
+ return wire.NewErrBoundToBatch(ctx)
}
// Check perms.
sn := d.st.NewSnapshot()
if err := util.GetWithAuth(ctx, call, sn, d.stKey(), &databaseData{}); err != nil {
sn.Abort()
- return nil, err
+ return err
}
- return util.Glob(ctx, call, "*", sn, sn.Abort, util.TablePrefix)
+ return util.Glob(ctx, call, matcher, sn, sn.Abort, util.TablePrefix)
}
////////////////////////////////////////
diff --git a/services/syncbase/server/nosql/table.go b/services/syncbase/server/nosql/table.go
index 558a1c1..a3ed4b7 100644
--- a/services/syncbase/server/nosql/table.go
+++ b/services/syncbase/server/nosql/table.go
@@ -11,6 +11,7 @@
"v.io/syncbase/x/ref/services/syncbase/server/util"
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/verror"
@@ -312,15 +313,15 @@
}
}
-func (t *tableReq) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
- impl := func(sntx store.SnapshotOrTransaction, closeSntx func() error) (<-chan string, error) {
+func (t *tableReq) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, matcher *glob.Element) error {
+ impl := func(sntx store.SnapshotOrTransaction, closeSntx func() error) error {
// Check perms.
if err := t.checkAccess(ctx, call, sntx, ""); err != nil {
closeSntx()
- return nil, err
+ return err
}
// TODO(rogulenko): Check prefix permissions for children.
- return util.Glob(ctx, call, "*", sntx, closeSntx, util.JoinKeyParts(util.RowPrefix, t.name))
+ return util.Glob(ctx, call, matcher, sntx, closeSntx, util.JoinKeyParts(util.RowPrefix, t.name))
}
if t.d.batchId != nil {
return impl(t.d.batchReader(), func() error {
diff --git a/services/syncbase/server/service.go b/services/syncbase/server/service.go
index 5b87252..bafa388 100644
--- a/services/syncbase/server/service.go
+++ b/services/syncbase/server/service.go
@@ -19,6 +19,7 @@
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/syncbase/x/ref/services/syncbase/vsync"
"v.io/v23/context"
+ "v.io/v23/glob"
"v.io/v23/rpc"
"v.io/v23/security/access"
"v.io/v23/verror"
@@ -160,14 +161,14 @@
return data.Perms, util.FormatVersion(data.Version), nil
}
-func (s *service) GlobChildren__(ctx *context.T, call rpc.ServerCall) (<-chan string, error) {
+func (s *service) GlobChildren__(ctx *context.T, call rpc.GlobChildrenServerCall, matcher *glob.Element) error {
// Check perms.
sn := s.st.NewSnapshot()
if err := util.GetWithAuth(ctx, call, sn, s.stKey(), &serviceData{}); err != nil {
sn.Abort()
- return nil, err
+ return err
}
- return util.Glob(ctx, call, "*", sn, sn.Abort, util.AppPrefix)
+ return util.Glob(ctx, call, matcher, sn, sn.Abort, util.AppPrefix)
}
////////////////////////////////////////
diff --git a/services/syncbase/server/util/glob.go b/services/syncbase/server/util/glob.go
index 2cbc3f9..8cf15a5 100644
--- a/services/syncbase/server/util/glob.go
+++ b/services/syncbase/server/util/glob.go
@@ -5,91 +5,36 @@
package util
import (
- "strings"
-
"v.io/syncbase/x/ref/services/syncbase/store"
"v.io/v23/context"
+ "v.io/v23/glob"
+ "v.io/v23/naming"
"v.io/v23/rpc"
- "v.io/v23/verror"
"v.io/x/lib/vlog"
- "v.io/x/ref/lib/glob"
)
// NOTE(nlacasse): Syncbase handles Glob requests by implementing
// GlobChildren__ at each level (service, app, database, table).
-//
-// Each GlobChildren__ method returns all the children of the name without
-// filtering them in any way. Any filtering that needs to happen based on the
-// glob pattern happens magically at a higher level. This results in a simple
-// GlobChildren__ implementation, but often an inefficient one, since we must
-// return every single child name, even though many will be filtered before
-// being sent to the client.
-//
-// We should consider changing GlobChildren__ so that it takes the pattern that
-// is being Globbed as an argument. Then, GlobChildren__ could be smarter about
-// what it returns, and only return names that already match the pattern. This
-// would prevent us from having to read all child names from the database every
-// time we Glob.
-
-// globPatternToPrefix takes "foo*" and returns "foo".
-// It assumes the input pattern is a valid glob pattern, and returns
-// verror.ErrBadArg if the input is not a valid prefix.
-func globPatternToPrefix(pattern string) (string, error) {
- if pattern == "" {
- return "", verror.NewErrBadArg(nil)
- }
- if pattern[len(pattern)-1] != '*' {
- return "", verror.NewErrBadArg(nil)
- }
- res := pattern[:len(pattern)-1]
- // Disallow chars and char sequences that have special meaning in glob, since
- // our Glob() does not support these.
- if strings.ContainsAny(res, "/*?[") {
- return "", verror.NewErrBadArg(nil)
- }
- if strings.Contains(res, "...") {
- return "", verror.NewErrBadArg(nil)
- }
- return res, nil
-}
// Glob performs a glob. It calls closeSntx to close sntx.
-// TODO(sadovsky): Why do we make developers implement Glob differently from
-// other streaming RPCs? It's confusing that Glob must return immediately and
-// write its results to a channel, while other streaming RPC handlers must block
-// and write their results to the output stream. See nlacasse's TODO below, too.
-func Glob(ctx *context.T, call rpc.ServerCall, pattern string, sntx store.SnapshotOrTransaction, closeSntx func() error, stKeyPrefix string) (<-chan string, error) {
- // TODO(sadovsky): Support glob with non-prefix pattern.
- if _, err := glob.Parse(pattern); err != nil {
- closeSntx()
- return nil, verror.New(verror.ErrBadArg, ctx, err)
- }
- prefix, err := globPatternToPrefix(pattern)
- if err != nil {
- closeSntx()
- if verror.ErrorID(err) == verror.ErrBadArg.ID {
- return nil, verror.NewErrNotImplemented(ctx)
- }
- return nil, verror.New(verror.ErrInternal, ctx, err)
- }
+func Glob(ctx *context.T, call rpc.GlobChildrenServerCall, matcher *glob.Element, sntx store.SnapshotOrTransaction, closeSntx func() error, stKeyPrefix string) error {
+ prefix, _ := matcher.FixedPrefix()
it := sntx.Scan(ScanPrefixArgs(stKeyPrefix, prefix))
- ch := make(chan string)
- go func() {
- defer closeSntx()
- defer close(ch)
- key := []byte{}
- for it.Advance() {
- key = it.Key(key)
- parts := SplitKeyParts(string(key))
- ch <- parts[len(parts)-1]
+ defer closeSntx()
+ key := []byte{}
+ for it.Advance() {
+ key = it.Key(key)
+ parts := SplitKeyParts(string(key))
+ name := parts[len(parts)-1]
+ if matcher.Match(name) {
+ if err := call.SendStream().Send(naming.GlobChildrenReplyName{name}); err != nil {
+ return err
+ }
}
- if err := it.Err(); err != nil {
- // TODO(nlacasse): We should do something with the error other than
- // just logging. Ideally we could send the error back to the
- // client, but the GlobChildren__ API doesn't really allow for that
- // since it uses a channel of strings rather than GlobResults.
- vlog.VI(1).Infof("Glob(%q) failed: %v", pattern, err)
- }
- }()
- return ch, nil
+ }
+ if err := it.Err(); err != nil {
+ vlog.VI(1).Infof("Glob() failed: %v", err)
+ call.SendStream().Send(naming.GlobChildrenReplyError{naming.GlobError{Error: err}})
+ }
+ return nil
}
diff --git a/services/syncbase/server/util/glob_test.go b/services/syncbase/server/util/glob_test.go
deleted file mode 100644
index d9c8b71..0000000
--- a/services/syncbase/server/util/glob_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-// 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.
-
-// Note, we use package util rather than util_test so that we can access the
-// unexported name util.globPatternToPrefix.
-
-package util
-
-import (
- "testing"
-
- "v.io/v23/verror"
-)
-
-var (
- errBadArg = string(verror.ErrBadArg.ID)
-)
-
-func TestGlobPatternToPrefix(t *testing.T) {
- tests := []struct {
- pattern, prefix string
- }{
- {"", errBadArg},
- {"*", ""},
- {"foo", errBadArg},
- {"foo*", "foo"},
- {"foo/", errBadArg},
- {"foo/*", errBadArg},
- {"foo/bar", errBadArg},
- {"foo/bar*", errBadArg},
- {".*", "."},
- {"..*", ".."},
- {"...*", errBadArg},
- {"...foo*", errBadArg},
- {"foo...foo*", errBadArg},
- {"..foo*", "..foo"},
- {"foo..foo*", "foo..foo"},
- {"...", errBadArg},
- {"*/...", errBadArg},
- {"foo/...", errBadArg},
- {"/", errBadArg},
- {"/*", errBadArg},
- }
- for _, test := range tests {
- prefix, err := globPatternToPrefix(test.pattern)
- if err != nil {
- prefix = string(verror.ErrorID(err))
- }
- if prefix != test.prefix {
- t.Errorf("%q: got %q, want %q", test.pattern, prefix, test.prefix)
- }
- }
-}