Bug 1283083 - Cloning a connection should re-attach databases to the clone. r=asuth
authorMarco Bonardo <mbonardo@mozilla.com>
Wed, 29 Jun 2016 17:33:15 +0200
changeset 343398 9b5be9da64bafc31baba3bc81c5d42785081e3d9
parent 343397 21585b3e48141dc0ecb55fece8f424ba5855b08f
child 343399 20d8f274cc880211d59708b569358cc70134aff5
push id6389
push userraliiev@mozilla.com
push dateMon, 19 Sep 2016 13:38:22 +0000
treeherdermozilla-beta@01d67bfe6c81 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1283083
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1283083 - Cloning a connection should re-attach databases to the clone. r=asuth MozReview-Commit-ID: CSZqvJNWthB
storage/mozIStorageAsyncConnection.idl
storage/mozIStorageConnection.idl
storage/mozStorageConnection.cpp
storage/test/unit/test_storage_connection.js
--- a/storage/mozIStorageAsyncConnection.idl
+++ b/storage/mozIStorageAsyncConnection.idl
@@ -42,16 +42,17 @@ interface mozIStorageAsyncConnection : n
    *         If called on a connection that has already been closed or was
    *         never properly opened.  The callback will still be dispatched
    *         to the main thread despite the returned error.
    */
   void asyncClose([optional] in mozIStorageCompletionCallback aCallback);
 
   /**
    * Clone a database and make the clone read only if needed.
+   * SQL Functions and attached on-disk databases are applied to the new clone.
    *
    * @param aReadOnly
    *        If true, the returned database should be put into read-only mode.
    *
    * @param aCallback
    *        A callback that will be notified when the operation is complete,
    *        with the following arguments:
    *        - status: the status of the operation
--- a/storage/mozIStorageConnection.idl
+++ b/storage/mozIStorageConnection.idl
@@ -53,16 +53,17 @@ interface mozIStorageConnection : mozISt
    *         If any statement has been executed asynchronously on this object.
    * @throws NS_ERROR_UNEXPECTED
    *         If is called on a thread other than the one that opened it.
    */
   void close();
 
   /**
    * Clones a database connection and makes the clone read only if needed.
+   * SQL Functions and attached on-disk databases are applied to the new clone.
    *
    * @param aReadOnly
    *        If true, the returned database should be put into read-only mode.
    *        Defaults to false.
    * @return the cloned database connection.
    *
    * @throws NS_ERROR_UNEXPECTED
    *         If this connection is a memory database.
--- a/storage/mozStorageConnection.cpp
+++ b/storage/mozStorageConnection.cpp
@@ -1329,16 +1329,39 @@ nsresult
 Connection::initializeClone(Connection* aClone, bool aReadOnly)
 {
   nsresult rv = mFileURL ? aClone->initialize(mFileURL)
                          : aClone->initialize(mDatabaseFile);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  // Re-attach on-disk databases that were attached to the original connection.
+  {
+    nsCOMPtr<mozIStorageStatement> stmt;
+    rv = CreateStatement(NS_LITERAL_CSTRING("PRAGMA database_list"),
+                         getter_AddRefs(stmt));
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    bool hasResult = false;
+    while (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
+      nsAutoCString name;
+      rv = stmt->GetUTF8String(1, name);
+      if (NS_SUCCEEDED(rv) && !name.Equals(NS_LITERAL_CSTRING("main")) &&
+                              !name.Equals(NS_LITERAL_CSTRING("temp"))) {
+        nsCString path;
+        rv = stmt->GetUTF8String(2, path);
+        if (NS_SUCCEEDED(rv) && !path.IsEmpty()) {
+          rv = aClone->ExecuteSimpleSQL(NS_LITERAL_CSTRING("ATTACH DATABASE '") +
+            path + NS_LITERAL_CSTRING("' AS ") + name);
+          MOZ_ASSERT(NS_SUCCEEDED(rv), "couldn't re-attach database to cloned connection");
+        }
+      }
+    }
+  }
+
   // Copy over pragmas from the original connection.
   static const char * pragmas[] = {
     "cache_size",
     "temp_store",
     "foreign_keys",
     "journal_size_limit",
     "synchronous",
     "wal_autocheckpoint",
--- a/storage/test/unit/test_storage_connection.js
+++ b/storage/test/unit/test_storage_connection.js
@@ -691,24 +691,58 @@ add_task(function* test_readonly_clone_c
     validate(pragma.value, stmt.getInt32(0));
     stmt.finalize();
   });
 
   db1.close();
   db2.close();
 });
 
+add_task(function* test_clone_attach_database() {
+  let db1 = getService().openUnsharedDatabase(getTestDB());
+
+  let c = 0;
+  function attachDB(conn, name) {
+    let file = dirSvc.get("ProfD", Ci.nsIFile);
+    file.append("test_storage_" + (++c) + ".sqlite");
+    let db = getService().openUnsharedDatabase(file);
+    conn.executeSimpleSQL(`ATTACH DATABASE '${db.databaseFile.path}' AS ${name}`);
+    db.close();
+  }
+  attachDB(db1, "attached_1");
+  attachDB(db1, "attached_2");
+
+  // These should not throw.
+  db1.createStatement("SELECT * FROM attached_1.sqlite_master");
+  db1.createStatement("SELECT * FROM attached_2.sqlite_master");
+
+  // R/W clone.
+  let db2 = db1.clone();
+  do_check_true(db2.connectionReady);
+
+  // These should not throw.
+  db2.createStatement("SELECT * FROM attached_1.sqlite_master");
+  db2.createStatement("SELECT * FROM attached_2.sqlite_master");
+
+  // R/O clone.
+  let db3 = db1.clone(true);
+  do_check_true(db3.connectionReady);
+
+  // These should not throw.
+  db3.createStatement("SELECT * FROM attached_1.sqlite_master");
+  db3.createStatement("SELECT * FROM attached_2.sqlite_master");
+
+  db1.close();
+  db2.close();
+  db3.close();
+});
+
 add_task(function* test_getInterface() {
   let db = getOpenedDatabase();
   let target = db.QueryInterface(Ci.nsIInterfaceRequestor)
                  .getInterface(Ci.nsIEventTarget);
   // Just check that target is non-null.  Other tests will ensure that it has
   // the correct value.
   do_check_true(target != null);
 
   yield asyncClose(db);
   gDBConn = null;
 });
-
-
-function run_test() {
-  run_next_test();
-}