From edd043b9624ec45177fb35d09d647feaae59ff10 Mon Sep 17 00:00:00 2001
From: Blair McBride <bmcbride@mozilla.com>
Date: Sat, 19 May 2012 17:18:38 +1200
Subject: [PATCH] Bug 752868 - Outdated locales aren't being removed from
 extensions.sqlite, causing bad performance. r=dtownsend

---
 toolkit/mozapps/extensions/XPIProvider.jsm    |   6 +-
 .../addons/test_db_sanity_1_1/install.rdf     |  58 ++++++
 .../addons/test_db_sanity_1_2/install.rdf     |  59 ++++++
 .../test/xpcshell/test_db_sanity.js           | 181 ++++++++++++++++++
 .../extensions/test/xpcshell/xpcshell.ini     |   1 +
 5 files changed, 303 insertions(+), 2 deletions(-)
 create mode 100644 toolkit/mozapps/extensions/test/addons/test_db_sanity_1_1/install.rdf
 create mode 100644 toolkit/mozapps/extensions/test/addons/test_db_sanity_1_2/install.rdf
 create mode 100644 toolkit/mozapps/extensions/test/xpcshell/test_db_sanity.js

diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm
index 729f36f4d1f22..75e006a4f0cb5 100644
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -129,7 +129,7 @@ const PREFIX_NS_EM                    = "http://www.mozilla.org/2004/em-rdf#";
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
-const DB_SCHEMA                       = 12;
+const DB_SCHEMA                       = 13;
 
 // Properties that exist in the install manifest
 const PROP_METADATA      = ["id", "version", "type", "internalName", "updateURL",
@@ -4750,6 +4750,8 @@ var XPIDatabase = {
                                   "homepageURL TEXT");
       this.connection.createTable("locale_strings",
                                   "locale_id INTEGER, type TEXT, value TEXT");
+      this.connection.executeSimpleSQL("CREATE INDEX locale_strings_idx ON " +
+        "locale_strings (locale_id)");
       this.connection.executeSimpleSQL("CREATE TRIGGER delete_addon AFTER DELETE " +
         "ON addon BEGIN " +
         "DELETE FROM targetApplication WHERE addon_internal_id=old.internal_id; " +
@@ -5415,7 +5417,7 @@ var XPIDatabase = {
         aLocale.locales.forEach(function(aName) {
           stmt.params.internal_id = internal_id;
           stmt.params.name = aName;
-          stmt.params.locale = insertLocale(aLocale);
+          stmt.params.locale = id;
           executeStatement(stmt);
         });
       });
diff --git a/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_1/install.rdf b/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_1/install.rdf
new file mode 100644
index 0000000000000..e1f2b51735a2b
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_1/install.rdf
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>test_db_sanity_1@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+    <em:bootstrap>true</em:bootstrap>
+
+    <em:name>Test 1</em:name>
+    <em:description>Test Description</em:description>
+    <em:creator>Keyboard Cat</em:creator>
+    <em:homepageURL>http://mozilla.org/</em:homepageURL>
+
+    <em:contributor>Keyboard Cat 2</em:contributor>
+    <em:translator>Keyboard Cat 3</em:translator>
+
+    <em:localized>
+      <Description>
+        <em:locale>en-1</em:locale>
+        <em:name>Test 1 (en-1)</em:name>
+        <em:description>Test Description (en-1)</em:description>
+        <em:creator>Keyboard Cat (en-1)</em:creator>
+        <em:homepageURL>http://mozilla.org/en-1/</em:homepageURL>
+      </Description>
+    </em:localized>
+
+    <em:localized>
+      <Description>
+        <em:locale>en-2</em:locale>
+        <em:name>Test 1 (en-2)</em:name>
+        <em:description>Test Description (en-2)</em:description>
+        <em:creator>Keyboard Cat (en-2)</em:creator>
+        <em:homepageURL>http://mozilla.org/en-2/</em:homepageURL>
+      </Description>
+    </em:localized>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>1</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>keyboard-cats-awesome-browser@keyboard.cat</em:id>
+        <em:minVersion>3.1415</em:minVersion>
+        <em:maxVersion>3.1415</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+    <em:targetPlatform>XPCShell_noarch-spidermonkey</em:targetPlatform>
+    <em:targetPlatform>WINNT_x86</em:targetPlatform>
+  </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_2/install.rdf b/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_2/install.rdf
new file mode 100644
index 0000000000000..da9b067ab60b5
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_db_sanity_1_2/install.rdf
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>test_db_sanity_1@tests.mozilla.org</em:id>
+    <em:version>2.0</em:version>
+    <em:bootstrap>true</em:bootstrap>
+
+    <em:name>Test 1</em:name>
+    <em:description>Test Description!!!</em:description>
+    <em:creator>Keyboard Cat</em:creator>
+    <em:homepageURL>http://mozilla.org/</em:homepageURL>
+
+    <em:contributor>Keyboard Cat 2</em:contributor>
+    <em:translator>Keyboard Cat 3</em:translator>
+    <em:translator>Keyboard Cat 4</em:translator>
+
+    <em:localized>
+      <Description>
+        <em:locale>en-1</em:locale>
+        <em:name>Test 1 (en-1)</em:name>
+        <em:description>Test Description (en-1)</em:description>
+        <em:creator>Keyboard Cat (en-1)</em:creator>
+        <em:homepageURL>http://mozilla.org/en-1/</em:homepageURL>
+      </Description>
+    </em:localized>
+
+    <em:localized>
+      <Description>
+        <em:locale>en-3</em:locale>
+        <em:name>Test 1 (en-3)</em:name>
+        <em:description>Test Description (en-3)</em:description>
+        <em:creator>Keyboard Cat (en-3)</em:creator>
+        <em:homepageURL>http://mozilla.org/en-3/</em:homepageURL>
+      </Description>
+    </em:localized>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>1</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>keyboard-cats-awesome-browser-3000@keyboard.cat</em:id>
+        <em:minVersion>3.1415</em:minVersion>
+        <em:maxVersion>3.1415</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+    <em:targetPlatform>XPCShell_noarch-spidermonkey</em:targetPlatform>
+    <em:targetPlatform>WINNT_i386</em:targetPlatform>
+  </Description>
+</RDF>
diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_db_sanity.js b/toolkit/mozapps/extensions/test/xpcshell/test_db_sanity.js
new file mode 100644
index 0000000000000..693ca42cda98a
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_db_sanity.js
@@ -0,0 +1,181 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// This tests the data in extensions.sqlite for general sanity, making sure
+// rows in one table only reference rows in another table that actually exist.
+
+
+function check_db() {
+  do_print("Checking DB sanity...");
+  var dbfile = gProfD.clone();
+  dbfile.append("extensions.sqlite");
+  var db = Services.storage.openDatabase(dbfile);
+
+  do_print("Checking locale_strings references rows in locale correctly...");
+  let localeStringsStmt = db.createStatement("SELECT * FROM locale_strings");
+  let localeStmt = db.createStatement("SELECT COUNT(*) AS count FROM locale WHERE id=:locale_id");
+  let i = 0;
+  while (localeStringsStmt.executeStep()) {
+    i++;
+    localeStmt.params.locale_id = localeStringsStmt.row.locale_id;
+    do_check_true(localeStmt.executeStep());
+    do_check_eq(localeStmt.row.count, 1);
+    localeStmt.reset();
+  }
+  localeStmt.finalize();
+  localeStringsStmt.finalize();
+  do_print("Done. " + i + " rows in locale_strings checked.");
+
+
+  do_print("Checking locale references rows in addon_locale and addon correctly...");
+  localeStmt = db.createStatement("SELECT * FROM locale");
+  let addonLocaleStmt = db.createStatement("SELECT COUNT(*) AS count FROM addon_locale WHERE locale_id=:locale_id");
+  let addonStmt = db.createStatement("SELECT COUNT(*) AS count FROM addon WHERE defaultLocale=:locale_id");
+  i = 0;
+  while (localeStmt.executeStep()) {
+    i++;
+    addonLocaleStmt.params.locale_id = localeStmt.row.id;
+    do_check_true(addonLocaleStmt.executeStep());
+    if (addonLocaleStmt.row.count == 0) {
+      addonStmt.params.locale_id = localeStmt.row.id;
+      do_check_true(addonStmt.executeStep());
+      do_check_eq(addonStmt.row.count, 1);
+    } else {
+      do_check_eq(addonLocaleStmt.row.count, 1);
+    }
+    addonLocaleStmt.reset();
+    addonStmt.reset();
+  }
+  addonLocaleStmt.finalize();
+  localeStmt.finalize();
+  addonStmt.finalize();
+  do_print("Done. " + i + " rows in locale checked.");
+
+
+  do_print("Checking addon_locale references rows in locale correctly...");
+  addonLocaleStmt = db.createStatement("SELECT * FROM addon_locale");
+  localeStmt = db.createStatement("SELECT COUNT(*) AS count FROM locale WHERE id=:locale_id");
+  i = 0;
+  while (addonLocaleStmt.executeStep()) {
+    i++;
+    localeStmt.params.locale_id = addonLocaleStmt.row.locale_id;
+    do_check_true(localeStmt.executeStep());
+    do_check_eq(localeStmt.row.count, 1);
+    localeStmt.reset();
+  }
+  addonLocaleStmt.finalize();
+  localeStmt.finalize();
+  do_print("Done. " + i + " rows in addon_locale checked.");
+
+
+  do_print("Checking addon_locale references rows in addon correctly...");
+  addonLocaleStmt = db.createStatement("SELECT * FROM addon_locale");
+  addonStmt = db.createStatement("SELECT COUNT(*) AS count FROM addon WHERE internal_id=:addon_internal_id");
+  i = 0;
+  while (addonLocaleStmt.executeStep()) {
+    i++;
+    addonStmt.params.addon_internal_id = addonLocaleStmt.row.addon_internal_id;
+    do_check_true(addonStmt.executeStep());
+    do_check_eq(addonStmt.row.count, 1);
+    addonStmt.reset();
+  }
+  addonLocaleStmt.finalize();
+  addonStmt.finalize();
+  do_print("Done. " + i + " rows in addon_locale checked.");
+
+
+  do_print("Checking addon references rows in locale correctly...");
+  addonStmt = db.createStatement("SELECT * FROM addon");
+  localeStmt = db.createStatement("SELECT COUNT(*) AS count FROM locale WHERE id=:defaultLocale");
+  i = 0;
+  while (addonStmt.executeStep()) {
+    i++;
+    localeStmt.params.defaultLocale = addonStmt.row.defaultLocale;
+    do_check_true(localeStmt.executeStep());
+    do_check_eq(localeStmt.row.count, 1);
+    localeStmt.reset();
+  }
+  addonStmt.finalize();
+  localeStmt.finalize();
+  do_print("Done. " + i + " rows in addon checked.");
+
+
+  do_print("Checking targetApplication references rows in addon correctly...");
+  let targetAppStmt = db.createStatement("SELECT * FROM targetApplication");
+  addonStmt = db.createStatement("SELECT COUNT(*) AS count FROM addon WHERE internal_id=:addon_internal_id");
+  i = 0;
+  while (targetAppStmt.executeStep()) {
+    i++;
+    addonStmt.params.addon_internal_id = targetAppStmt.row.addon_internal_id;
+    do_check_true(addonStmt.executeStep());
+    do_check_eq(addonStmt.row.count, 1);
+    addonStmt.reset();
+  }
+  targetAppStmt.finalize();
+  addonStmt.finalize();
+  do_print("Done. " + i + " rows in targetApplication checked.");
+
+
+  do_print("Checking targetPlatform references rows in addon correctly...");
+  let targetPlatformStmt = db.createStatement("SELECT * FROM targetPlatform");
+  addonStmt = db.createStatement("SELECT COUNT(*) AS count FROM addon WHERE internal_id=:addon_internal_id");
+  i = 0;
+  while (targetPlatformStmt.executeStep()) {
+    i++;
+    addonStmt.params.addon_internal_id = targetPlatformStmt.row.addon_internal_id;
+    do_check_true(addonStmt.executeStep());
+    do_check_eq(addonStmt.row.count, 1);
+    addonStmt.reset();
+  }
+  targetPlatformStmt.finalize();
+  addonStmt.finalize();
+  do_print("Done. " + i + " rows in targetPlatform checked.");
+
+
+  db.close();
+  do_print("Done checking DB sanity.");
+}
+
+function run_test() {
+  do_test_pending();
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
+  startupManager();
+
+  installAllFiles([do_get_addon("test_db_sanity_1_1")], run_test_1);
+}
+
+function run_test_1() {
+  shutdownManager();
+  check_db();
+  startupManager();
+
+  AddonManager.getAddonByID("test_db_sanity_1@tests.mozilla.org", function(aAddon) {
+    aAddon.uninstall();
+
+    shutdownManager();
+    check_db();
+    startupManager();
+
+    installAllFiles([do_get_addon("test_db_sanity_1_1")], run_test_2);
+  });
+}
+
+function run_test_2() {
+  installAllFiles([do_get_addon("test_db_sanity_1_2")], function() {
+    shutdownManager();
+    check_db();
+    startupManager();
+    run_test_3();
+  });
+}
+
+function run_test_3() {
+  AddonManager.getAddonByID("test_db_sanity_1@tests.mozilla.org", function(aAddon) {
+    aAddon.uninstall();
+
+    shutdownManager();
+    check_db();
+
+    do_test_finished();
+  });
+}
diff --git a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
index 0f865afbab264..bedf2a8ab6184 100644
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -135,6 +135,7 @@ fail-if = os == "android"
 [test_compatoverrides.js]
 [test_corrupt.js]
 [test_corrupt_strictcompat.js]
+[test_db_sanity.js]
 [test_dictionary.js]
 [test_disable.js]
 [test_distribution.js]
-- 
GitLab