Backed out changeset a01ced6de845 (bug 702559)
authorEd Morley <emorley@mozilla.com>
Wed, 26 Jun 2013 12:56:14 +0100
changeset 136504 906f40e126fa47de4abb920478b449dcccf496c2
parent 136503 0cd729c9f1a4b3ee432e5d5d82c434c89c5af43b
child 136505 f4c77fefe4981c3b55a469931e4c2e476edf681b
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs702559
milestone25.0a1
backs outa01ced6de845bfdbf79c0ef443859a0379181927
Backed out changeset a01ced6de845 (bug 702559)
toolkit/modules/Sqlite.jsm
toolkit/modules/tests/xpcshell/test_sqlite.js
--- a/toolkit/modules/Sqlite.jsm
+++ b/toolkit/modules/Sqlite.jsm
@@ -21,17 +21,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
                                   "resource://gre/modules/Task.jsm");
 
 
 // Counts the number of created connections per database basename(). This is
 // used for logging to distinguish connection instances.
-let connectionCounters = new Map();
+let connectionCounters = {};
 
 
 /**
  * Opens a connection to a SQLite database.
  *
  * The following parameters can control the connection:
  *
  *   path -- (string) The filesystem path of the database file to open. If the
@@ -84,47 +84,44 @@ function openConnection(options) {
                       "Got: " + options.shrinkMemoryOnConnectionIdleMS);
     }
 
     openedOptions.shrinkMemoryOnConnectionIdleMS =
       options.shrinkMemoryOnConnectionIdleMS;
   }
 
   let file = FileUtils.File(path);
+  let openDatabaseFn = sharedMemoryCache ?
+                         Services.storage.openDatabase :
+                         Services.storage.openUnsharedDatabase;
 
   let basename = OS.Path.basename(path);
-  let number = connectionCounters.get(basename) || 0;
-  connectionCounters.set(basename, number + 1);
 
+  if (!connectionCounters[basename]) {
+    connectionCounters[basename] = 1;
+  }
+
+  let number = connectionCounters[basename]++;
   let identifier = basename + "#" + number;
 
   log.info("Opening database: " + path + " (" + identifier + ")");
-  let deferred = Promise.defer();
-  let options = null;
-  if (!sharedMemoryCache) {
-    options = Cc["@mozilla.org/hash-property-bag;1"].
-      createInstance(Ci.nsIWritablePropertyBag);
-    options.setProperty("shared", false);
-  }
-  Services.storage.openAsyncDatabase(file, options, function(status, connection) {
-    if (!connection) {
-      log.warn("Could not open connection: " + status);
-      deferred.reject(new Error("Could not open connection: " + status));
+  try {
+    let connection = openDatabaseFn(file);
+
+    if (!connection.connectionReady) {
+      log.warn("Connection is not ready.");
+      return Promise.reject(new Error("Connection is not ready."));
     }
-    log.warn("Connection opened");
-    try {
-      deferred.resolve(
-        new OpenedConnection(connection.QueryInterface(Ci.mozIStorageAsyncConnection), basename, number,
-        openedOptions));
-    } catch (ex) {
-      log.warn("Could not open database: " + CommonUtils.exceptionStr(ex));
-      deferred.reject(ex);
-    }
-  });
-  return deferred.promise;
+
+    return Promise.resolve(new OpenedConnection(connection, basename, number,
+                                                openedOptions));
+  } catch (ex) {
+    log.warn("Could not open database: " + CommonUtils.exceptionStr(ex));
+    return Promise.reject(ex);
+  }
 }
 
 
 /**
  * Handle on an opened SQLite database.
  *
  * This is essentially a glorified wrapper around mozIStorageConnection.
  * However, it offers some compelling advantages.
@@ -223,42 +220,61 @@ function OpenedConnection(connection, ba
 
 OpenedConnection.prototype = Object.freeze({
   TRANSACTION_DEFERRED: "DEFERRED",
   TRANSACTION_IMMEDIATE: "IMMEDIATE",
   TRANSACTION_EXCLUSIVE: "EXCLUSIVE",
 
   TRANSACTION_TYPES: ["DEFERRED", "IMMEDIATE", "EXCLUSIVE"],
 
+  get connectionReady() {
+    return this._open && this._connection.connectionReady;
+  },
+
+  /**
+   * The row ID from the last INSERT operation.
+   *
+   * Because all statements are executed asynchronously, this could
+   * return unexpected results if multiple statements are performed in
+   * parallel. It is the caller's responsibility to schedule
+   * appropriately.
+   *
+   * It is recommended to only use this within transactions (which are
+   * handled as sequential statements via Tasks).
+   */
+  get lastInsertRowID() {
+    this._ensureOpen();
+    return this._connection.lastInsertRowID;
+  },
+
+  /**
+   * The number of rows that were changed, inserted, or deleted by the
+   * last operation.
+   *
+   * The same caveats regarding asynchronous execution for
+   * `lastInsertRowID` also apply here.
+   */
+  get affectedRows() {
+    this._ensureOpen();
+    return this._connection.affectedRows;
+  },
+
   /**
    * The integer schema version of the database.
    *
    * This is 0 if not schema version has been set.
-   *
-   * @return Promise<int>
    */
-  getSchemaVersion: function() {
-    let self = this;
-    return this.execute("PRAGMA user_version").then(
-      function onSuccess(result) {
-        if (result == null) {
-          return 0;
-        }
-        return JSON.stringify(result[0].getInt32(0));
-      }
-    );
+  get schemaVersion() {
+    this._ensureOpen();
+    return this._connection.schemaVersion;
   },
 
-  setSchemaVersion: function(value) {
-    if (!Number.isInteger(value)) {
-      // Guarding against accidental SQLi
-      throw new TypeError("Schema version must be an integer. Got " + value);
-    }
+  set schemaVersion(value) {
     this._ensureOpen();
-    return this.execute("PRAGMA user_version = " + value);
+    this._connection.schemaVersion = value;
   },
 
   /**
    * Close the database connection.
    *
    * This must be performed when you are finished with the database.
    *
    * Closing the database connection has the side effect of forcefully
--- a/toolkit/modules/tests/xpcshell/test_sqlite.js
+++ b/toolkit/modules/tests/xpcshell/test_sqlite.js
@@ -82,44 +82,16 @@ add_task(function test_open_unshared() {
 
 add_task(function test_get_dummy_database() {
   let db = yield getDummyDatabase("get_dummy_database");
 
   do_check_eq(typeof(db), "object");
   yield db.close();
 });
 
-add_task(function test_schema_version() {
-  let db = yield getDummyDatabase("schema_version");
-
-  let version = yield db.getSchemaVersion();
-  do_check_eq(version, 0);
-
-  db.setSchemaVersion(14);
-  version = yield db.getSchemaVersion();
-  do_check_eq(version, 14);
-
-  for (let v of [0.5, "foobar", NaN]) {
-    let success;
-    try {
-      yield db.setSchemaVersion(v);
-      do_print("Schema version " + v + " should have been rejected");
-      success = false;
-    } catch (ex if ex.message.startsWith("Schema version must be an integer.")) {
-      success = true;
-    }
-    do_check_true(success);
-
-    version = yield db.getSchemaVersion();
-    do_check_eq(version, 14);
-  }
-
-  yield db.close();
-});
-
 add_task(function test_simple_insert() {
   let c = yield getDummyDatabase("simple_insert");
 
   let result = yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
   do_check_true(Array.isArray(result));
   do_check_eq(result.length, 0);
   yield c.close();
 });
@@ -132,20 +104,18 @@ add_task(function test_simple_bound_arra
   yield c.close();
 });
 
 add_task(function test_simple_bound_object() {
   let c = yield getDummyDatabase("simple_bound_object");
   let result = yield c.execute("INSERT INTO dirs VALUES (:id, :path)",
                                {id: 1, path: "foo"});
   do_check_eq(result.length, 0);
-  result = yield c.execute("SELECT id, path FROM dirs");
-  do_check_eq(result.length, 1);
-  do_check_eq(result[0].getResultByName("id"), 1);
-  do_check_eq(result[0].getResultByName("path"), "foo");
+  do_check_eq(c.lastInsertRowID, 1);
+  do_check_eq(c.affectedRows, 1);
   yield c.close();
 });
 
 // This is mostly a sanity test to ensure simple executions work.
 add_task(function test_simple_insert_then_select() {
   let c = yield getDummyDatabase("simple_insert_then_select");
 
   yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");