Bug 691509 - Run ANALYZE at each schema change (and force a schema change).
authorMarco Bonardo <mbonardo@mozilla.com>
Fri, 07 Oct 2011 21:10:44 +0200
changeset 76345 b2f7b1fac9fbed519b25ee1c92b061ed60e755bc
parent 76344 dd943de3de11b1b3e4f12f5c00ea20c4a551a222
child 76346 77d2910ba5144250e873bad6a8eef12bc3b62a4f
push id290
push usermak77@bonardo.net
push dateWed, 19 Oct 2011 00:10:12 +0000
treeherdermozilla-beta@77d2910ba514 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs691509
milestone8.0
Bug 691509 - Run ANALYZE at each schema change (and force a schema change). Partly copied from a patch by Richard Newman. r=dietrich a=LegNeato
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsPlacesExpiration.js
toolkit/components/places/tests/head_common.js
toolkit/components/places/tests/migration/test_current_from_v10.js
toolkit/components/places/tests/migration/test_current_from_v10_migrated_from_v11.js
toolkit/components/places/tests/migration/test_v11_from_v10.js
toolkit/components/places/tests/migration/test_v11_from_v10_migrated_from_v11.js
toolkit/components/places/tests/migration/xpcshell.ini
toolkit/components/places/tests/unit/test_analyze.js
toolkit/components/places/tests/unit/xpcshell.ini
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -145,17 +145,17 @@ using namespace mozilla::places;
 // crashes we could lose all the transactions in the file.  But a too small
 // file could hurt performance.
 #define DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES 512
 
 #define BYTES_PER_MEBIBYTE 1048576
 
 // This is the schema version, update it at any schema change and add a
 // corresponding migrateVxx method below.
-#define DATABASE_SCHEMA_VERSION 11
+#define DATABASE_SCHEMA_VERSION 12
 
 // Filename of the database.
 #define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite")
 
 // Filename used to backup corrupt databases.
 #define DATABASE_CORRUPT_FILENAME NS_LITERAL_STRING("places.sqlite.corrupt")
 
 // In order to avoid calling PR_now() too often we use a cached "now" value
@@ -297,16 +297,59 @@ namespace mozilla {
                "AND t_t.parent = ") +
                nsPrintfCString("%lld", aTagsFolder) + NS_LITERAL_CSTRING(" "
              ")"));
       }
 
       _sqlFragment.AppendLiteral(" AS tags ");
     }
 
+    /**
+     * Updates sqlite_stat1 table through ANALYZE.
+     * Since also nsPlacesExpiration.js executes ANALYZE, the analyzed tables
+     * must be the same in both components.  So ensure they are in sync.
+     */
+    nsresult updateSQLiteStatistics(mozIStorageConnection* aDBConn)
+    {
+      nsCOMPtr<mozIStorageAsyncStatement> analyzePlacesStmt;
+      nsresult rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
+        "ANALYZE moz_places"
+      ), getter_AddRefs(analyzePlacesStmt));
+      NS_ENSURE_SUCCESS(rv, rv);
+      nsCOMPtr<mozIStorageAsyncStatement> analyzeBookmarksStmt;
+      rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
+        "ANALYZE moz_bookmarks"
+      ), getter_AddRefs(analyzeBookmarksStmt));
+      NS_ENSURE_SUCCESS(rv, rv);
+      nsCOMPtr<mozIStorageAsyncStatement> analyzeVisitsStmt;
+      rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
+        "ANALYZE moz_historyvisits"
+      ), getter_AddRefs(analyzeVisitsStmt));
+      NS_ENSURE_SUCCESS(rv, rv);
+      nsCOMPtr<mozIStorageAsyncStatement> analyzeInputStmt;
+      rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
+        "ANALYZE moz_inputhistory"
+      ), getter_AddRefs(analyzeInputStmt));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      mozIStorageBaseStatement *stmts[] = {
+        analyzePlacesStmt,
+        analyzeBookmarksStmt,
+        analyzeVisitsStmt,
+        analyzeInputStmt
+      };
+
+      nsCOMPtr<mozIStoragePendingStatement> ps;
+      rv = aDBConn->ExecuteAsync(stmts, NS_ARRAY_LENGTH(stmts), nsnull,
+                                 getter_AddRefs(ps));
+      NS_ENSURE_SUCCESS(rv, rv);
+
+      return NS_OK;
+    }
+
   } // namespace places
 } // namespace mozilla
 
 
 namespace {
 
 /**
  * This class sets begin/end of batch updates to correspond to C++ scopes so
@@ -873,16 +916,19 @@ nsNavHistory::InitDB()
     rv = nsAnnotationService::InitTables(mDBConn);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Set the schema version to the current one.
   rv = UpdateSchemaVersion();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = updateSQLiteStatistics(mDBConn);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   ForceWALCheckpoint(mDBConn);
 
   // ANY FAILURE IN THIS METHOD WILL CAUSE US TO MARK THE DATABASE AS CORRUPT
   // AND TRY TO REPLACE IT.
   // DO NOT PUT HERE ANYTHING THAT IS NOT RELATED TO INITIALIZATION OR MODIFYING
--- a/toolkit/components/places/nsPlacesExpiration.js
+++ b/toolkit/components/places/nsPlacesExpiration.js
@@ -402,16 +402,18 @@ const EXPIRATION_QUERIES = {
     sql: "DELETE FROM expiration_notify",
     actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN |
              ACTION.IDLE | ACTION.DEBUG
   },
 
   // The following queries are used to adjust the sqlite_stat1 table to help the
   // query planner create better queries.  These should always be run LAST, and
   // are therefore at the end of the object.
+  // Since also nsNavHistory.cpp executes ANALYZE, the analyzed tables
+  // must be the same in both components.  So ensure they are in sync.
 
   QUERY_ANALYZE_MOZ_PLACES: {
     sql: "ANALYZE moz_places",
     actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY | ACTION.IDLE |
              ACTION.DEBUG
   },
   QUERY_ANALYZE_MOZ_BOOKMARKS: {
     sql: "ANALYZE moz_bookmarks",
--- a/toolkit/components/places/tests/head_common.js
+++ b/toolkit/components/places/tests/head_common.js
@@ -30,16 +30,18 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+const CURRENT_SCHEMA_VERSION = 12;
+
 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
 const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
 const NS_APP_BOOKMARKS_50_FILE = "BMarks";
 
 // Shortcuts to transitions type.
 const TRANSITION_LINK = Ci.nsINavHistoryService.TRANSITION_LINK;
 const TRANSITION_TYPED = Ci.nsINavHistoryService.TRANSITION_TYPED;
 const TRANSITION_BOOKMARK = Ci.nsINavHistoryService.TRANSITION_BOOKMARK;
rename from toolkit/components/places/tests/migration/test_v11_from_v10.js
rename to toolkit/components/places/tests/migration/test_current_from_v10.js
--- a/toolkit/components/places/tests/migration/test_v11_from_v10.js
+++ b/toolkit/components/places/tests/migration/test_current_from_v10.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * This file tests migration invariants from schema version 10 to schema version
- * 11.
+ * This file tests migration invariants from schema version 10 to the current
+ * schema version.
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 const kGuidAnnotationName = "sync/guid";
 const kExpectedAnnotations = 5;
 const kExpectedValidGuids = 2;
@@ -291,17 +291,17 @@ function test_final_state()
     // WAL journal mode should be set on this database.
     do_check_eq(stmt.getString(0).toLowerCase(), "wal");
     stmt.finalize();
   }
 
   do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex"));
   do_check_true(db.indexExists("moz_places_guid_uniqueindex"));
 
-  do_check_eq(db.schemaVersion, 11);
+  do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION);
 
   db.close();
   run_next_test();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Test Runner
 
rename from toolkit/components/places/tests/migration/test_v11_from_v10_migrated_from_v11.js
rename to toolkit/components/places/tests/migration/test_current_from_v10_migrated_from_v11.js
--- a/toolkit/components/places/tests/migration/test_v11_from_v10_migrated_from_v11.js
+++ b/toolkit/components/places/tests/migration/test_current_from_v10_migrated_from_v11.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * This file tests migration invariants from a database with schema version 11
  * that was then downgraded to a database with a schema version 10.  Places
- * should then migrate this database to one with a schema version of 11.
+ * should then migrate this database to one with the current schema version.
  */
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Test Functions
 
 function test_initial_state()
 {
   // Mostly sanity checks our starting DB to make sure it's setup as we expect
@@ -104,17 +104,17 @@ function test_place_guids_non_null()
 function test_final_state()
 {
   // We open a new database mostly so that we can check that the settings were
   // actually saved.
   let dbFile = gProfD.clone();
   dbFile.append(kDBName);
   let db = Services.storage.openUnsharedDatabase(dbFile);
 
-  do_check_eq(db.schemaVersion, 11);
+  do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION);
 
   db.close();
   run_next_test();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Test Runner
 
--- a/toolkit/components/places/tests/migration/xpcshell.ini
+++ b/toolkit/components/places/tests/migration/xpcshell.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
 head = head_migration.js
 tail = 
 
-[test_v11_from_v10.js]
-[test_v11_from_v10_migrated_from_v11.js]
+[test_current_from_v10.js]
+[test_current_from_v10_migrated_from_v11.js]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/unit/test_analyze.js
@@ -0,0 +1,28 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests sqlite_sta1 table exists, it should be created by analyze.
+// Note that this version of SQLite puts null rows for empty tables, while next
+// versions will just omit the rows.
+
+function run_test() {
+  do_test_pending();
+
+  let stmt = DBConn().createAsyncStatement(
+    "SELECT ROWID FROM sqlite_stat1"
+  );
+  stmt.executeAsync({
+    _gotResult: false,
+    handleResult: function(aResultSet) {
+      this._gotResult = true;
+    },
+    handleError: function(aError) {
+      do_throw("Unexpected error (" + aError.result + "): " + aError.message);
+    },
+    handleCompletion: function(aReason) {
+      do_check_true(this._gotResult);
+      do_test_finished();
+    }
+  });
+  stmt.finalize();
+}
--- a/toolkit/components/places/tests/unit/xpcshell.ini
+++ b/toolkit/components/places/tests/unit/xpcshell.ini
@@ -33,16 +33,17 @@ tail =
 [test_454977.js]
 [test_457698_crash.js]
 [test_463863.js]
 [test_485442_crash_bug_nsNavHistoryQuery_GetUri.js]
 [test_486978_sort_by_date_queries.js]
 [test_536081.js]
 [test_adaptive.js]
 [test_adaptive_bug527311.js]
+[test_analyze.js]
 [test_annotations.js]
 [test_asyncExecuteLegacyQueries.js]
 [test_async_history_api.js]
 [test_autocomplete_stopSearch_no_throw.js]
 [test_bookmark_catobs.js]
 [test_bookmarks_setNullTitle.js]
 [test_broken_folderShortcut_result.js]
 [test_browserhistory.js]