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';