syncbase: switch to /$/ hierarchy separator scheme
For detailed description, see http://v.io/c/15374
MultiPart: 3/5
Change-Id: I2938fce9088ea8df390905022738ab09e0ab3c16
diff --git a/src/app.js b/src/app.js
index db24f6c..ed9a1db 100644
--- a/src/app.js
+++ b/src/app.js
@@ -12,12 +12,22 @@
module.exports = App;
+/**
+ * @summary
+ * App represents a collection of Databases.
+ * Private constructor. Use service.app() to get an instance.
+ * @param {string} parentFullName Full name of parent Service.
+ * @param {string} relativeName Relative name for this App.
+ * @constructor
+ * @inner
+ * @memberof {module:syncbase}
+ */
function App(parentFullName, relativeName) {
if (!(this instanceof App)) {
return new App(parentFullName, relativeName);
}
- util.addNameProperties(this, parentFullName, relativeName);
+ util.addNameProperties(this, parentFullName, relativeName, false);
// TODO(nlacasse): Use the prr module to simplify all the
// 'Object.defineProperty' calls scattered throughout the project.
@@ -43,7 +53,7 @@
// listDatabases returns of all database names.
App.prototype.listDatabases = function(ctx, cb) {
- util.getChildNames(ctx, this.fullName, cb);
+ this._wire(ctx).listDatabases(ctx, cb);
};
// create creates this app. If perms is empty, we inherit (copy) the Service
diff --git a/src/gen-vdl/v.io/v23/services/syncbase/index.js b/src/gen-vdl/v.io/v23/services/syncbase/index.js
index aaf142d..b086ab2 100644
--- a/src/gen-vdl/v.io/v23/services/syncbase/index.js
+++ b/src/gen-vdl/v.io/v23/services/syncbase/index.js
@@ -20,6 +20,11 @@
// Types:
+var _type1 = new vdl.Type();
+_type1.kind = vdl.kind.LIST;
+_type1.name = "";
+_type1.elem = vdl.types.STRING;
+_type1.freeze();
@@ -48,6 +53,11 @@
+Service.prototype.listApps = function(ctx, serverCall) {
+ throw new Error('Method ListApps not implemented');
+};
+
+
Service.prototype.setPermissions = function(ctx, serverCall, perms, version) {
throw new Error('Method SetPermissions not implemented');
};
@@ -73,6 +83,22 @@
{
+ name: 'ListApps',
+ doc: "// ListApps returns a list of all App names.",
+ inArgs: [],
+ outArgs: [{
+ name: '',
+ doc: "",
+ type: _type1
+ },
+ ],
+ inStream: null,
+ outStream: null,
+ tags: [canonicalize.reduce(new access.Tag("Read", true), new access.Tag()._type), ]
+ },
+
+
+ {
name: 'SetPermissions',
doc: "// SetPermissions replaces the current Permissions for an object. version\n// allows for optional, optimistic concurrency control. If non-empty,\n// version's value must come from GetPermissions. If any client has\n// successfully called SetPermissions in the meantime, the version will be\n// stale and SetPermissions will fail. If empty, SetPermissions performs an\n// unconditional update.\n//\n// Permissions objects are expected to be small. It is up to the\n// implementation to define the exact limit, though it should probably be\n// around 100KB. Large lists of principals can be represented concisely using\n// blessings.\n//\n// There is some ambiguity when calling SetPermissions on a mount point.\n// Does it affect the mount itself or does it affect the service endpoint\n// that the mount points to? The chosen behavior is that it affects the\n// service endpoint. To modify the mount point's Permissions, use\n// ResolveToMountTable to get an endpoint and call SetPermissions on that.\n// This means that clients must know when a name refers to a mount point to\n// change its Permissions.",
inArgs: [{
@@ -138,6 +164,11 @@
};
+App.prototype.listDatabases = function(ctx, serverCall) {
+ throw new Error('Method ListDatabases not implemented');
+};
+
+
App.prototype.setPermissions = function(ctx, serverCall, perms, version) {
throw new Error('Method SetPermissions not implemented');
};
@@ -206,6 +237,22 @@
{
+ name: 'ListDatabases',
+ doc: "// ListDatabases returns a list of all Database names.\n// TODO(kash): Include the database type (NoSQL vs. SQL).",
+ inArgs: [],
+ outArgs: [{
+ name: '',
+ doc: "",
+ type: _type1
+ },
+ ],
+ inStream: null,
+ outStream: null,
+ tags: [canonicalize.reduce(new access.Tag("Read", true), new access.Tag()._type), ]
+ },
+
+
+ {
name: 'SetPermissions',
doc: "// SetPermissions replaces the current Permissions for an object. version\n// allows for optional, optimistic concurrency control. If non-empty,\n// version's value must come from GetPermissions. If any client has\n// successfully called SetPermissions in the meantime, the version will be\n// stale and SetPermissions will fail. If empty, SetPermissions performs an\n// unconditional update.\n//\n// Permissions objects are expected to be small. It is up to the\n// implementation to define the exact limit, though it should probably be\n// around 100KB. Large lists of principals can be represented concisely using\n// blessings.\n//\n// There is some ambiguity when calling SetPermissions on a mount point.\n// Does it affect the mount itself or does it affect the service endpoint\n// that the mount points to? The chosen behavior is that it affects the\n// service endpoint. To modify the mount point's Permissions, use\n// ResolveToMountTable to get an endpoint and call SetPermissions on that.\n// This means that clients must know when a name refers to a mount point to\n// change its Permissions.",
inArgs: [{
diff --git a/src/gen-vdl/v.io/v23/services/syncbase/nosql/index.js b/src/gen-vdl/v.io/v23/services/syncbase/nosql/index.js
index 83246c6..e144175 100644
--- a/src/gen-vdl/v.io/v23/services/syncbase/nosql/index.js
+++ b/src/gen-vdl/v.io/v23/services/syncbase/nosql/index.js
@@ -299,7 +299,7 @@
DatabaseWatcher.prototype._serviceDescription = {
name: 'DatabaseWatcher',
pkgPath: 'v.io/v23/services/syncbase/nosql',
- doc: "// DatabaseWatcher allows a client to watch for updates in the database.\n// For each watched request, the client will receive a reliable stream of watch\n// events without re-ordering. See watch.GlobWatcher for a detailed explanation\n// of the behavior.\n// TODO(rogulenko): Currently the only supported watch patterns are\n// 'table/row*'. Consider changing that.\n//\n// The watching is done by starting a streaming RPC. The argument to the RPC\n// contains the ResumeMarker that points to a particular place in the database\n// event log. The result stream consists of a never-ending sequence of Change\n// messages (until the call fails or is canceled). Each Change contains the\n// Name field in the form \"<tableName>/<rowKey>\" and the Value field of the\n// StoreChange type. If the client has no access to a row specified in a change,\n// that change is excluded from the result stream.\n//\n// The DatabaseWatcher is designed to be used in the following way:\n// 1) begin a read-only batch\n// 2) read all information your app needs\n// 3) read the ResumeMarker\n// 4) abort the batch\n// 5) start watching changes to the data using the ResumeMarker\n// In this configuration the client doesn't miss any changes.",
+ doc: "// DatabaseWatcher allows a client to watch for updates to the database. For\n// each watch request, the client will receive a reliable stream of watch events\n// without re-ordering. See watch.GlobWatcher for a detailed explanation of the\n// behavior.\n// TODO(rogulenko): Currently the only supported watch patterns are\n// \"<tableName>/$/<rowPrefix>*\". Consider changing that.\n//\n// The watching is done by starting a streaming RPC. The argument to the RPC\n// contains the ResumeMarker that points to a particular place in the database\n// event log. The result stream consists of a never-ending sequence of Change\n// messages (until the call fails or is canceled). Each Change contains the\n// Name field in the form \"<tableName>/<rowKey>\" and the Value field of the\n// StoreChange type. If the client has no access to a row specified in a change,\n// that change is excluded from the result stream.\n//\n// The DatabaseWatcher is designed to be used in the following way:\n// 1) begin a read-only batch\n// 2) read all information your app needs\n// 3) read the ResumeMarker\n// 4) abort the batch\n// 5) start watching changes to the data using the ResumeMarker\n// In this configuration the client doesn't miss any changes.",
embeds: [{
name: 'GlobWatcher',
pkgPath: 'v.io/v23/services/watch',
@@ -672,7 +672,7 @@
{
name: 'CreateBlob',
- doc: "// API for resumable blob creation (append-only). After commit, a blob\n// is immutable. Before commit, the BlobRef can be used with PutBlob,\n// GetBlobSize, DeleteBlob, and CommitBlob. After commit, PutBlob and\n// CommitBlob can no longer be used. Blob creation can be resumed by\n// obtaining the current blob size with GetBlobSize and appending to the\n// blob via PutBlob.\n//\n// CreateBlob returns a BlobRef for a newly created blob.",
+ doc: "// CreateBlob returns a BlobRef for a newly created blob.",
inArgs: [],
outArgs: [{
name: 'br',
@@ -786,7 +786,7 @@
{
name: 'FetchBlob',
- doc: "// FetchBlob initiates fetching a blob if not locally found. priority\n// controls the network priority of the blob. Higher priority blobs are\n// fetched before the lower priority ones. However an ongoing blob\n// transfer is not interrupted. Status updates are streamed back to the\n// client as fetch is in progress.",
+ doc: "// FetchBlob initiates fetching a blob if not locally found. priority\n// controls the network priority of the blob. Higher priority blobs are\n// fetched before the lower priority ones. However, an ongoing blob\n// transfer is not interrupted. Status updates are streamed back to the\n// client as fetch is in progress.",
inArgs: [{
name: 'br',
doc: "",
@@ -988,6 +988,11 @@
};
+Database.prototype.listTables = function(ctx, serverCall) {
+ throw new Error('Method ListTables not implemented');
+};
+
+
Database.prototype.exec = function(ctx, serverCall, schemaVersion, query) {
throw new Error('Method Exec not implemented');
};
@@ -1151,7 +1156,7 @@
{
name: 'DatabaseWatcher',
pkgPath: 'v.io/v23/services/syncbase/nosql',
- doc: "// DatabaseWatcher allows a client to watch for updates in the database.\n// For each watched request, the client will receive a reliable stream of watch\n// events without re-ordering. See watch.GlobWatcher for a detailed explanation\n// of the behavior.\n// TODO(rogulenko): Currently the only supported watch patterns are\n// 'table/row*'. Consider changing that.\n//\n// The watching is done by starting a streaming RPC. The argument to the RPC\n// contains the ResumeMarker that points to a particular place in the database\n// event log. The result stream consists of a never-ending sequence of Change\n// messages (until the call fails or is canceled). Each Change contains the\n// Name field in the form \"<tableName>/<rowKey>\" and the Value field of the\n// StoreChange type. If the client has no access to a row specified in a change,\n// that change is excluded from the result stream.\n//\n// The DatabaseWatcher is designed to be used in the following way:\n// 1) begin a read-only batch\n// 2) read all information your app needs\n// 3) read the ResumeMarker\n// 4) abort the batch\n// 5) start watching changes to the data using the ResumeMarker\n// In this configuration the client doesn't miss any changes."
+ doc: "// DatabaseWatcher allows a client to watch for updates to the database. For\n// each watch request, the client will receive a reliable stream of watch events\n// without re-ordering. See watch.GlobWatcher for a detailed explanation of the\n// behavior.\n// TODO(rogulenko): Currently the only supported watch patterns are\n// \"<tableName>/$/<rowPrefix>*\". Consider changing that.\n//\n// The watching is done by starting a streaming RPC. The argument to the RPC\n// contains the ResumeMarker that points to a particular place in the database\n// event log. The result stream consists of a never-ending sequence of Change\n// messages (until the call fails or is canceled). Each Change contains the\n// Name field in the form \"<tableName>/<rowKey>\" and the Value field of the\n// StoreChange type. If the client has no access to a row specified in a change,\n// that change is excluded from the result stream.\n//\n// The DatabaseWatcher is designed to be used in the following way:\n// 1) begin a read-only batch\n// 2) read all information your app needs\n// 3) read the ResumeMarker\n// 4) abort the batch\n// 5) start watching changes to the data using the ResumeMarker\n// In this configuration the client doesn't miss any changes."
},
{
name: 'SyncGroupManager',
@@ -1236,6 +1241,22 @@
{
+ name: 'ListTables',
+ doc: "// ListTables returns a list of all Table names.",
+ inArgs: [],
+ outArgs: [{
+ name: '',
+ doc: "",
+ type: _type1
+ },
+ ],
+ inStream: null,
+ outStream: null,
+ tags: [canonicalize.reduce(new access.Tag("Read", true), new access.Tag()._type), ]
+ },
+
+
+ {
name: 'Exec',
doc: "// Exec executes a syncQL query and returns all results as specified by in the\n// query's select clause. Concurrency semantics are documented in model.go.",
inArgs: [{
@@ -1592,7 +1613,7 @@
{
name: 'CreateBlob',
- doc: "// API for resumable blob creation (append-only). After commit, a blob\n// is immutable. Before commit, the BlobRef can be used with PutBlob,\n// GetBlobSize, DeleteBlob, and CommitBlob. After commit, PutBlob and\n// CommitBlob can no longer be used. Blob creation can be resumed by\n// obtaining the current blob size with GetBlobSize and appending to the\n// blob via PutBlob.\n//\n// CreateBlob returns a BlobRef for a newly created blob.",
+ doc: "// CreateBlob returns a BlobRef for a newly created blob.",
inArgs: [],
outArgs: [{
name: 'br',
@@ -1706,7 +1727,7 @@
{
name: 'FetchBlob',
- doc: "// FetchBlob initiates fetching a blob if not locally found. priority\n// controls the network priority of the blob. Higher priority blobs are\n// fetched before the lower priority ones. However an ongoing blob\n// transfer is not interrupted. Status updates are streamed back to the\n// client as fetch is in progress.",
+ doc: "// FetchBlob initiates fetching a blob if not locally found. priority\n// controls the network priority of the blob. Higher priority blobs are\n// fetched before the lower priority ones. However, an ongoing blob\n// transfer is not interrupted. Status updates are streamed back to the\n// client as fetch is in progress.",
inArgs: [{
name: 'br',
doc: "",
@@ -1951,7 +1972,7 @@
{
name: 'DeleteRange',
- doc: "// DeleteRange deletes all rows in the given half-open range [start, limit).\n// If limit is \"\", all rows with keys >= start are included.\n// TODO(sadovsky): Delete prefix perms fully covered by the row range?",
+ doc: "// DeleteRange deletes all rows in the given half-open range [start, limit).\n// If limit is \"\", all rows with keys >= start are included.\n// TODO(sadovsky): Maybe add option to delete prefix perms fully covered by\n// the row range.",
inArgs: [{
name: 'schemaVersion',
doc: "",
diff --git a/src/nosql/database.js b/src/nosql/database.js
index b4ff441..34ca05b 100644
--- a/src/nosql/database.js
+++ b/src/nosql/database.js
@@ -2,11 +2,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-module.exports = Database;
-
var through2 = require('through2');
var vanadium = require('vanadium');
-// TODO(nlacasse): We should put unwrap and other type util methods on
+// TODO(nlacasse): We should expose unwrap and other type-util methods on the
// vanadium.vdl object.
var unwrap = require('vanadium/src/vdl/type-util').unwrap;
var verror = vanadium.verror;
@@ -24,22 +22,26 @@
var util = require('../util');
var watch = require('./watch');
+module.exports = Database;
+
/**
+ * @summary
* Database represents a collection of Tables. Batches, queries, sync, watch,
* etc. all operate at the Database level.
+ * Private constructor. Use app.noSqlDatabase() to get an instance.
+ * @param {string} parentFullName Full name of parent App.
+ * @param {string} relativeName Relative name for this Database.
+ * @param {number} schemaVersion Database schema version expected by client.
* @constructor
- * @param {string} parentFullName Full name of App which contains this
- * Database.
- * @param {string} relativeName Relative name of this Database. Must not
- * contain slashes.
- * @param {module:syncbase.schema.Schema} schema Schema for the database.
+ * @inner
+ * @memberof {module:syncbase.nosql}
*/
function Database(parentFullName, relativeName, schema) {
if (!(this instanceof Database)) {
return new Database(parentFullName, relativeName);
}
- util.addNameProperties(this, parentFullName, relativeName);
+ util.addNameProperties(this, parentFullName, relativeName, true);
Object.defineProperty(this, 'schema', {
enumerable: false,
@@ -177,18 +179,14 @@
* @param {function} cb Callback.
*/
Database.prototype.listTables = function(ctx, cb) {
- util.getChildNames(ctx, this.fullName, cb);
+ this._wire(ctx).listTables(ctx, cb);
};
/**
* @private
*/
Database.prototype._tableWire = function(ctx, relativeName) {
- if (relativeName.indexOf('/') >= 0) {
- throw new Error('relativeName must not contain slashes.');
- }
-
- var client = vanadium.runtimeForContext(ctx).getClient();
+ var client = vanadium.runtimeForContext(ctx).newClient();
var signature = [nosqlVdl.Table.prototype._serviceDescription];
var fullTableName = vanadium.naming.join(this.fullName, relativeName);
@@ -219,7 +217,7 @@
*/
Database.prototype.watch = function(ctx, tableName, prefix, resumeMarker, cb) {
var globReq = new watchVdl.GlobRequest({
- pattern: vanadium.naming.join(tableName, prefix + '*'),
+ pattern: vanadium.naming.join(tableName, util.NAME_SEP, prefix + '*'),
resumeMarker: resumeMarker
});
@@ -238,8 +236,11 @@
return cb(new Error('invalid change state ' + change.state));
}
+ // NOTE(sadovsky): We call stripBasename twice to convert "<table>/$/<row>"
+ // to "<table>".
var wc = new watch.WatchChange({
- tableName: vanadium.naming.stripBasename(change.name),
+ tableName: vanadium.naming.stripBasename(
+ vanadium.naming.stripBasename(change.name)),
rowName: vanadium.naming.basename(change.name),
changeType: changeType,
valueBytes: changeType === 'put' ? change.value.value : null,
@@ -252,6 +253,11 @@
var stream = this._wire(ctx).watchGlob(ctx, globReq, cb).stream;
+ // TODO(sadovsky): Our JS watch test times out after 20s when pattern is
+ // "<table>/<prefix>*", i.e. when it's missing the "/$/" separator. That's
+ // strange, because the server should immediately return an RPC error (since
+ // util.ParseTableRowPair returns an error). Does the JS watch test not check
+ // for this error?
var watchChangeStream = stream.pipe(watchChangeEncoder);
stream.on('error', function(err) {
watchChangeStream.emit('error', err);
@@ -481,4 +487,3 @@
return cb(null, new Blob(self, blobRef));
});
};
-
diff --git a/src/nosql/row.js b/src/nosql/row.js
index f662a43..ae6f31a 100644
--- a/src/nosql/row.js
+++ b/src/nosql/row.js
@@ -5,30 +5,30 @@
var vanadium = require('vanadium');
var nosqlVdl = require('../gen-vdl/v.io/v23/services/syncbase/nosql');
+var util = require('../util');
module.exports = Row;
/**
* @summary
- * Represents a single row in a Table.
- * Private constructor, use table.row() to get an instance.
+ * Row represents a single row in a Table.
+ * Private constructor. Use table.row() to get an instance.
+ * @param {string} parentFullName Full name of parent Table.
+ * @param {string} key Key for this Row.
* @param {number} schemaVersion Database schema version expected by client.
- * @inner
* @constructor
- * @memberof module:syncbase.nosql
+ * @inner
+ * @memberof {module:syncbase.nosql}
*/
function Row(parentFullName, key, schemaVersion) {
if (!(this instanceof Row)) {
return new Row(parentFullName, key, schemaVersion);
}
- // TODO(aghassemi) We may need to escape the key. Align with Go implementation
- // when that decision is made.
- // Also for Database and Table, we throw error if name has a slash.
- // Should they all behave the same or is row key really different?
- var fullName = vanadium.naming.join(parentFullName, key);
+ util.addNameProperties(this, parentFullName, key, true);
this.schemaVersion = schemaVersion;
+
/**
* The key of this Row.
* @property name
@@ -41,16 +41,6 @@
});
/**
- * @property name
- * @type {string}
- */
- Object.defineProperty(this, 'fullName', {
- value: fullName,
- writable: false,
- enumerable: true
- });
-
- /**
* Caches the table wire object.
* @private
*/
diff --git a/src/nosql/rowrange.js b/src/nosql/rowrange.js
index c85c9ad..d859cc9 100644
--- a/src/nosql/rowrange.js
+++ b/src/nosql/rowrange.js
@@ -29,7 +29,6 @@
function range(start, limit) {
var startBytes = util.stringToUTF8Bytes(start);
var limitBytes = util.stringToUTF8Bytes(limit);
-
return new RowRange(startBytes, limitBytes);
}
diff --git a/src/nosql/table.js b/src/nosql/table.js
index 344bd90..63a870c 100644
--- a/src/nosql/table.js
+++ b/src/nosql/table.js
@@ -8,21 +8,18 @@
var nosqlVdl = require('../gen-vdl/v.io/v23/services/syncbase/nosql');
var Row = require('./row');
var RowRange = require('./rowrange');
+var util = require('../util');
var prefix = RowRange.prefix;
module.exports = Table;
-var util = require('../util');
-
/**
* @summary
* Table represents a collection of Rows.
* Private constructor. Use database.table() to get an instance.
- * @param {string} parentFullName Full name of Database which contains this
- * Table.
- * @param {string} relativeName Relative name of this Table. Must not
- * contain slashes.
+ * @param {string} parentFullName Full name of parent Database.
+ * @param {string} relativeName Relative name for this Table.
* @param {number} schemaVersion Database schema version expected by client.
* @constructor
* @inner
@@ -33,7 +30,7 @@
return new Table(parentFullName, relativeName, schemaVersion);
}
- util.addNameProperties(this, parentFullName, relativeName);
+ util.addNameProperties(this, parentFullName, relativeName, true);
this.schemaVersion = schemaVersion;
diff --git a/src/service.js b/src/service.js
index 6f48ac9..72f7088 100644
--- a/src/service.js
+++ b/src/service.js
@@ -5,7 +5,6 @@
var vanadium = require('vanadium');
var App = require('./app');
-var util = require('./util');
var vdl = require('./gen-vdl/v.io/v23/services/syncbase');
// TODO(aghassemi): This looks clunky,
@@ -14,6 +13,14 @@
module.exports = Service;
+/**
+ * @summary
+ * Service represents a collection of Apps.
+ * @param {string} fullName Full Vanadium object name of this Service.
+ * @constructor
+ * @inner
+ * @memberof {module:syncbase}
+ */
function Service(fullName) {
if (!(this instanceof Service)) {
return new Service(fullName);
@@ -48,7 +55,7 @@
// listApps returns a list of all app names.
Service.prototype.listApps = function(ctx, cb) {
- util.getChildNames(ctx, this.fullName, cb);
+ this._wire(ctx).listApps(ctx, cb);
};
Service.prototype.getPermissions = function(ctx, cb) {
diff --git a/src/util.js b/src/util.js
index f9ad015..06e26c6 100644
--- a/src/util.js
+++ b/src/util.js
@@ -2,28 +2,29 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-var inherits = require('inherits');
var vanadium = require('vanadium');
+var NAME_SEP = '$';
+
module.exports = {
addNameProperties: addNameProperties,
- getChildNames: getChildNames,
prefixRangeLimit: prefixRangeLimit,
- InvalidNameError: InvalidNameError,
- stringToUTF8Bytes: stringToUTF8Bytes
+ stringToUTF8Bytes: stringToUTF8Bytes,
+ NAME_SEP: NAME_SEP
};
/**
* Creates the 'name' and 'fullName' properties on an object.
* @private
*/
-function addNameProperties(self, parentFullName, relativeName) {
- if (relativeName.indexOf('/') >= 0) {
- throw new InvalidNameError(relativeName);
+function addNameProperties(self, parentFullName, relativeName, addNameSep) {
+ var fullName;
+ if (addNameSep) {
+ fullName = vanadium.naming.join(parentFullName, NAME_SEP, relativeName);
+ } else {
+ fullName = vanadium.naming.join(parentFullName, relativeName);
}
- var fullName = vanadium.naming.join(parentFullName, relativeName);
-
/**
* @property _parentFullName
* @private
@@ -56,66 +57,22 @@
});
}
-function InvalidNameError(name) {
- Error.call(this);
- this.message = 'Invalid name "' + name + '". ' +
- ' Use vanadium.naming.encodeAsNamePart() to escape.';
-}
-inherits(InvalidNameError, Error);
-
/**
- * getChildNames returns all names that are children of the parentFullName.
- * @private
- */
-function getChildNames(ctx, parentFullName, cb) {
- var rt = vanadium.runtimeForContext(ctx);
- var namespace = rt.getNamespace();
- var childNames = [];
-
- var globPattern = vanadium.naming.join(parentFullName, '*');
-
- var streamErr = null;
-
- var stream = namespace.glob(ctx, globPattern, function(err) {
- if (err) {
- return cb(err);
- }
-
- if (streamErr) {
- return cb(streamErr);
- }
-
- cb(null, childNames);
- }).stream;
-
- stream.on('data', function(globResult) {
- var fullName = globResult.name;
- var name = vanadium.naming.basename(fullName);
- childNames.push(name);
- });
-
- stream.on('error', function(err) {
- console.error('Stream error: ' + JSON.stringify(err));
- // Store the first stream error in streamErr.
- streamErr = streamErr || err.error;
- });
-}
-
-/**
- * PrefixRangeLimit returns the limit of the row range for the given prefix.
+ * prefixRangeLimit modifies the input bytes to be the limit of the row range
+ * for the given prefix.
+ * TODO(sadovsky): Why do we modify the input bytes, rather than returning a new
+ * byte array?
* @private
* @param {Uint8Array} bytes Integer ArrayBuffer to modify.
*/
function prefixRangeLimit(bytes) {
- // For a given Uint8Array,
- // The code below effectively adds 1 to it, then chops off any
- // trailing \x00 bytes.
- // If the input string consists entirely of \xff bytes, we would empty out the
- // buffer
+ // bytes can be thought of as a base-256 number. The code below effectively
+ // adds 1 to this number, then chops off any trailing \x00 bytes. If the input
+ // string consists entirely of \xff bytes, we return an empty string.
while (bytes.length > 0) {
var last = bytes.length - 1;
if (bytes[last] === 255) {
- bytes = bytes.slice(0, last); // remove trailing \x00
+ bytes = bytes.slice(0, last); // chop off trailing \x00
} else {
bytes[last] += 1; // add 1
return; // no carry
diff --git a/test/integration/test-database.js b/test/integration/test-database.js
index e07669f..d9258e4 100644
--- a/test/integration/test-database.js
+++ b/test/integration/test-database.js
@@ -42,7 +42,7 @@
db.name = 'foo';
t.equal(db.name, dbName, 'Setting the name has no effect.');
- var expectedFullName = naming.join(o.app.fullName, dbName);
+ var expectedFullName = naming.join(o.app.fullName, '$', dbName);
t.equal(db.fullName, expectedFullName, 'Database has correct fullName.');
db.fullName = 'bar';
@@ -83,7 +83,7 @@
}
var dbName = 'bad/name';
- t.throws(function() {
+ t.doesNotThrow(function() {
o.app.noSqlDatabase(dbName);
}, 'should throw');
@@ -238,7 +238,7 @@
t.ok(table, 'table is created.');
t.ok(table instanceof Table, 'table is a Table object.');
t.equal(table.name, tableName, 'table has the correct name.');
- t.equal(table.fullName, vanadium.naming.join(db.fullName, tableName),
+ t.equal(table.fullName, vanadium.naming.join(db.fullName, '$', tableName),
'table has the correct fullName.');
o.teardown(t.end);
diff --git a/test/integration/test-table.js b/test/integration/test-table.js
index 86b2fdd..c82ede0 100644
--- a/test/integration/test-table.js
+++ b/test/integration/test-table.js
@@ -13,7 +13,7 @@
var setupTable = testUtil.setupTable;
var uniqueName = testUtil.uniqueName;
-//TODO(aghassemi): We fail to bind to Unicode names, investigate.
+// TODO(aghassemi): We fail to bind to Unicode names; investigate.
//var ROW_KEY = 'چשKEYઑᜰ';
//var ROW_VAL = '⛓⛸VALϦӪ';
var ROW_KEY = 'row_key';