Bug 1074817 - Allow downgrade of content-prefs.sqlite. r=MattN, a=lmandel
authorTomasz Kołodziejski <tkolodziejski@mozilla.com>
Fri, 10 Oct 2014 13:33:00 -0400
changeset 225750 2235079fe205
parent 225749 f19a52b7e6ec
child 225751 73bc0bc9343b
push id4000
push userryanvm@gmail.com
push date2014-10-20 16:31 +0000
treeherdermozilla-beta@6d2e5afd8b75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersMattN, lmandel
bugs1074817
milestone34.0
Bug 1074817 - Allow downgrade of content-prefs.sqlite. r=MattN, a=lmandel
toolkit/components/contentprefs/nsContentPrefService.js
--- a/toolkit/components/contentprefs/nsContentPrefService.js
+++ b/toolkit/components/contentprefs/nsContentPrefService.js
@@ -1161,56 +1161,62 @@ ContentPrefService.prototype = {
     aDBFile.remove(false);
 
     let dbConnection = this._dbCreate(aDBService, aDBFile);
 
     return dbConnection;
   },
 
   _dbMigrate: function ContentPrefService__dbMigrate(aDBConnection, aOldVersion, aNewVersion) {
-    if (this["_dbMigrate" + aOldVersion + "To" + aNewVersion]) {
-      aDBConnection.beginTransaction();
-      try {
-        this["_dbMigrate" + aOldVersion + "To" + aNewVersion](aDBConnection);
-        aDBConnection.schemaVersion = aNewVersion;
-        aDBConnection.commitTransaction();
+    /**
+     * Migrations have to follow a template rules in bug 1074817 comment 3 which are:
+     * 1. Migration have to be incremental and non-breaking.
+     * 2. It have to be idempotent because one can downgrade an upgrade again.
+     * On downgrade:
+     * 1. Decrement schema version so that upgrade runs the migrations again.
+     */
+    aDBConnection.beginTransaction();
+
+    try {
+      /**
+      * If the schema version is 0, that means it was never set, which means
+      * the database was somehow created without the schema being applied, perhaps
+      * because the system ran out of disk space (although we check for this
+      * in _createDB) or because some other code created the database file without
+      * applying the schema. In any case, recover by simply reapplying the schema.
+      */
+      if (aOldVersion == 0) {
+        this._dbCreateSchema(aDBConnection);
+      } else {
+        // For downgrade this is a no-op since aOldVersion > aNewVersion.
+        for (let i = aOldVersion; i < aNewVersion; i++) {
+          let migrationName = "_dbMigrate" + i + "To" + (i + 1);
+          if (typeof this[migrationName] != 'function') {
+            throw("no migrator function from version " + aOldVersion + " to version " + aNewVersion);
+          }
+          this[migrationName](aDBConnection);
+        }
       }
-      catch(ex) {
-        aDBConnection.rollbackTransaction();
-        throw ex;
-      }
+      aDBConnection.schemaVersion = aNewVersion;
+      aDBConnection.commitTransaction();
+    } catch (ex) {
+      aDBConnection.rollbackTransaction();
+      throw ex;
     }
-    else
-      throw("no migrator function from version " + aOldVersion +
-            " to version " + aNewVersion);
   },
 
-  /**
-   * If the schema version is 0, that means it was never set, which means
-   * the database was somehow created without the schema being applied, perhaps
-   * because the system ran out of disk space (although we check for this
-   * in _createDB) or because some other code created the database file without
-   * applying the schema.  In any case, recover by simply reapplying the schema.
-   */
-  _dbMigrate0To3: function ContentPrefService___dbMigrate0To3(aDBConnection) {
-    this._dbCreateSchema(aDBConnection);
-  },
-
-  _dbMigrate1To3: function ContentPrefService___dbMigrate1To3(aDBConnection) {
+  _dbMigrate1To2: function ContentPrefService___dbMigrate1To2(aDBConnection) {
     aDBConnection.executeSimpleSQL("ALTER TABLE groups RENAME TO groupsOld");
     aDBConnection.createTable("groups", this._dbSchema.tables.groups);
     aDBConnection.executeSimpleSQL(`
       INSERT INTO groups (id, name)
       SELECT id, name FROM groupsOld
     `);
-
-    aDBConnection.executeSimpleSQL("DROP TABLE groupers");
+    aDBConnection.executeSimpleSQL("DROP TABLE IF EXISTS groupers");
     aDBConnection.executeSimpleSQL("DROP TABLE groupsOld");
-
-    this._dbCreateIndices(aDBConnection);
   },
 
   _dbMigrate2To3: function ContentPrefService__dbMigrate2To3(aDBConnection) {
     this._dbCreateIndices(aDBConnection);
   },
 
   _parseGroupParam: function ContentPrefService__parseGroupParam(aGroup) {
     if (aGroup == null)