Bug 704855. Reduce fsyncs in Places & create/update root bookmarks in Database.cpp. r=mak
authorVladan Djeric <vdjeric@mozilla.com>
Tue, 13 Dec 2011 12:52:06 -0500
changeset 82857 dca42848a1e29210554e4b8a322ee6b1d6494864
parent 82856 0881bdd95e583c7a5185fb72b00f99e0cb3311f1
child 82858 d4aad9645f77eebf2099f35db3d070a159a210f5
push id21705
push usermbrubeck@mozilla.com
push dateSat, 17 Dec 2011 17:01:50 +0000
treeherdermozilla-central@d4aad9645f77 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs704855
milestone11.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 704855. Reduce fsyncs in Places & create/update root bookmarks in Database.cpp. r=mak
toolkit/components/places/Database.cpp
toolkit/components/places/Database.h
toolkit/components/places/nsNavBookmarks.cpp
toolkit/components/places/nsNavBookmarks.h
toolkit/components/places/tests/unit/test_analyze.js
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -97,16 +97,19 @@
 // file could hurt performance.
 #define DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES 512
 
 #define BYTES_PER_MEBIBYTE 1048576
 
 // Old Sync GUID annotation.
 #define SYNCGUID_ANNO NS_LITERAL_CSTRING("sync/guid")
 
+// Places string bundle, contains internationalized bookmark root names.
+#define PLACES_BUNDLE "chrome://places/locale/places.properties"
+
 using namespace mozilla;
 
 namespace mozilla {
 namespace places {
 
 namespace {
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -252,16 +255,87 @@ SetJournalMode(nsCOMPtr<mozIStorageConne
     }
     // This is an unknown journal.
     MOZ_ASSERT(true);
   }
 
   return JOURNAL_DELETE;
 }
 
+nsresult
+CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
+           const nsCString& aRootName,
+           const nsXPIDLString& titleString)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // The position of the new item in its folder.
+  static PRInt32 itemPosition = 0;
+
+  // A single creation timestamp for all roots so that the root folder's
+  // last modification time isn't earlier than its childrens' creation time.
+  static PRTime timestamp = 0;
+  if (!timestamp)
+    timestamp = PR_Now();
+
+  // Create a new bookmark folder for the root.
+  nsCOMPtr<mozIStorageStatement> stmt;
+  nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
+    "INSERT INTO moz_bookmarks "
+      "(type, position, title, dateAdded, lastModified, guid, parent) "
+    "VALUES (:item_type, :item_position, :item_title,"
+            ":date_added, :last_modified, GENERATE_GUID(),"
+            "IFNULL((SELECT id FROM moz_bookmarks WHERE parent = 0), 0))"
+  ), getter_AddRefs(stmt));
+  if (NS_FAILED(rv)) return rv;
+
+  rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"),
+                             nsINavBookmarksService::TYPE_FOLDER);
+  if (NS_FAILED(rv)) return rv;
+  rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"), itemPosition);
+  if (NS_FAILED(rv)) return rv;
+  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
+                                  NS_ConvertUTF16toUTF8(titleString));
+  if (NS_FAILED(rv)) return rv;
+  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), timestamp);
+  if (NS_FAILED(rv)) return rv;
+  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), timestamp);
+  if (NS_FAILED(rv)) return rv;
+  rv = stmt->Execute();
+  if (NS_FAILED(rv)) return rv;
+
+  // Create an entry in moz_bookmarks_roots to link the folder to the root.
+  nsCOMPtr<mozIStorageStatement> newRootStmt;
+  rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
+    "INSERT INTO moz_bookmarks_roots (root_name, folder_id) "
+    "VALUES (:root_name, "
+              "(SELECT id from moz_bookmarks WHERE "
+              " position = :item_position AND "
+              " parent = IFNULL((SELECT MIN(folder_id) FROM moz_bookmarks_roots), 0)))"
+  ), getter_AddRefs(newRootStmt));
+  if (NS_FAILED(rv)) return rv;
+
+  rv = newRootStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"),
+                                         aRootName);
+  if (NS_FAILED(rv)) return rv;
+  rv = newRootStmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"),
+                                    itemPosition);
+  if (NS_FAILED(rv)) return rv;
+  rv = newRootStmt->Execute();
+  if (NS_FAILED(rv)) return rv;
+
+  // The 'places' root is a folder containing the other roots.
+  // The first bookmark in a folder has position 0.
+  if (!aRootName.Equals("places"))
+    ++itemPosition;
+
+  return NS_OK;
+}
+
+
 } // Anonymous namespace
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Database
 
 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(Database, gDatabase)
 
 NS_IMPL_THREADSAFE_ISUPPORTS2(Database
@@ -652,16 +726,21 @@ Database::InitSchema(bool* aDatabaseMigr
       if (currentSchemaVersion < 15) {
         rv = MigrateV15Up();
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       // Firefox 11 uses schema version 14.
 
       // Schema Upgrades must add migration code here.
+
+      rv = UpdateBookmarkRootTitles();
+      // We don't want a broken localization to cause us to think
+      // the database is corrupt and needs to be replaced.
+      MOZ_ASSERT(NS_SUCCEEDED(rv));
     }
   }
   else {
     // This is a new database, so we have to create all the tables and indices.
 
     // moz_places.
     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -728,16 +807,20 @@ Database::InitSchema(bool* aDatabaseMigr
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ANNOS_PLACEATTRIBUTE);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // moz_items_annos.
     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ITEMS_ANNOS);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
     NS_ENSURE_SUCCESS(rv, rv);
+
+    // Initialize the bookmark roots in the new DB.
+    rv = CreateBookmarkRoots();
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Set the schema version to the current one.
   rv = mMainConn->SetSchemaVersion(DATABASE_SCHEMA_VERSION);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
@@ -748,16 +831,88 @@ Database::InitSchema(bool* aDatabaseMigr
   // AND TRY TO REPLACE IT.
   // DO NOT PUT HERE ANYTHING THAT IS NOT RELATED TO INITIALIZATION OR MODIFYING
   // THE DISK DATABASE.
 
   return NS_OK;
 }
 
 nsresult
+Database::CreateBookmarkRoots()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIStringBundleService> bundleService =
+    services::GetStringBundleService();
+  NS_ENSURE_STATE(bundleService);
+  nsCOMPtr<nsIStringBundle> bundle;
+  nsresult rv = bundleService->CreateBundle(PLACES_BUNDLE, getter_AddRefs(bundle));
+  if (NS_FAILED(rv)) return rv;
+
+  nsXPIDLString rootTitle;
+  // The first root's title is an empty string.
+  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("places"), rootTitle);
+  if (NS_FAILED(rv)) return rv;
+
+  // Fetch the internationalized folder name from the string bundle.
+  rv = bundle->GetStringFromName(NS_LITERAL_STRING("BookmarksMenuFolderTitle").get(),
+                                 getter_Copies(rootTitle));
+  if (NS_FAILED(rv)) return rv;
+  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("menu"), rootTitle);
+  if (NS_FAILED(rv)) return rv;
+
+  rv = bundle->GetStringFromName(NS_LITERAL_STRING("BookmarksToolbarFolderTitle").get(),
+                                 getter_Copies(rootTitle));
+  if (NS_FAILED(rv)) return rv;
+  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("toolbar"), rootTitle);
+  if (NS_FAILED(rv)) return rv;
+
+  rv = bundle->GetStringFromName(NS_LITERAL_STRING("TagsFolderTitle").get(),
+                                 getter_Copies(rootTitle));
+  if (NS_FAILED(rv)) return rv;
+  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("tags"), rootTitle);
+  if (NS_FAILED(rv)) return rv;
+
+  rv = bundle->GetStringFromName(NS_LITERAL_STRING("UnsortedBookmarksFolderTitle").get(),
+                                 getter_Copies(rootTitle));
+  if (NS_FAILED(rv)) return rv;
+  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("unfiled"), rootTitle);
+  if (NS_FAILED(rv)) return rv;
+
+#if DEBUG
+  nsCOMPtr<mozIStorageStatement> stmt;
+  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
+    "SELECT "
+      "(SELECT COUNT(*) FROM moz_bookmarks), "
+      "(SELECT COUNT(*) FROM moz_bookmarks_roots), "
+      "(SELECT SUM(position) FROM moz_bookmarks WHERE "
+        "id IN (SELECT folder_id FROM moz_bookmarks_roots))"
+  ), getter_AddRefs(stmt));
+  if (NS_FAILED(rv)) return rv;
+
+  bool hasResult;
+  rv = stmt->ExecuteStep(&hasResult);
+  if (NS_FAILED(rv)) return rv;
+  MOZ_ASSERT(hasResult);
+  PRInt32 bookmarkCount = 0;
+  rv = stmt->GetInt32(0, &bookmarkCount);
+  if (NS_FAILED(rv)) return rv;
+  PRInt32 rootCount = 0;
+  rv = stmt->GetInt32(1, &rootCount);
+  if (NS_FAILED(rv)) return rv;
+  PRInt32 positionSum = 0;
+  rv = stmt->GetInt32(2, &positionSum);
+  if (NS_FAILED(rv)) return rv;
+  MOZ_ASSERT(bookmarkCount == 5 && rootCount == 5 && positionSum == 6);
+#endif
+
+  return NS_OK;
+}
+
+nsresult
 Database::InitFunctions()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsresult rv = GetUnreversedHostFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = MatchAutoCompleteFunction::create(mMainConn);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -778,16 +933,74 @@ Database::InitTempTriggers()
   NS_ENSURE_SUCCESS(rv, rv);
   rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERDELETE_TRIGGER);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 nsresult
+Database::UpdateBookmarkRootTitles()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsCOMPtr<nsIStringBundleService> bundleService =
+    services::GetStringBundleService();
+  NS_ENSURE_STATE(bundleService);
+
+  nsCOMPtr<nsIStringBundle> bundle;
+  nsresult rv = bundleService->CreateBundle(PLACES_BUNDLE, getter_AddRefs(bundle));
+  if (NS_FAILED(rv)) return rv;
+
+  nsCOMPtr<mozIStorageAsyncStatement> stmt;
+  rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
+    "UPDATE moz_bookmarks SET title = :new_title WHERE id = "
+      "(SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = :root_name)"
+  ), getter_AddRefs(stmt));
+  if (NS_FAILED(rv)) return rv;
+
+  nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
+  rv = stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
+  if (NS_FAILED(rv)) return rv;
+
+  const char *rootNames[] = { "menu", "toolbar", "tags", "unfiled" };
+  const char *titleStringIDs[] = {
+    "BookmarksMenuFolderTitle", "BookmarksToolbarFolderTitle",
+    "TagsFolderTitle", "UnsortedBookmarksFolderTitle"
+  };
+
+  for (PRUint32 i = 0; i < ArrayLength(rootNames); ++i) {
+    nsXPIDLString title;
+    rv = bundle->GetStringFromName(NS_ConvertASCIItoUTF16(titleStringIDs[i]).get(),
+                                   getter_Copies(title));
+    if (NS_FAILED(rv)) return rv;
+
+    nsCOMPtr<mozIStorageBindingParams> params;
+    rv = paramsArray->NewBindingParams(getter_AddRefs(params));
+    if (NS_FAILED(rv)) return rv;
+    rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"),
+                                      nsDependentCString(rootNames[i]));
+    if (NS_FAILED(rv)) return rv;
+    rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("new_title"),
+                                      NS_ConvertUTF16toUTF8(title));
+    if (NS_FAILED(rv)) return rv;
+    rv = paramsArray->AddParams(params);
+    if (NS_FAILED(rv)) return rv;
+  }
+
+  rv = stmt->BindParameters(paramsArray);
+  if (NS_FAILED(rv)) return rv;
+  nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
+  rv = stmt->ExecuteAsync(nsnull, getter_AddRefs(pendingStmt));
+  if (NS_FAILED(rv)) return rv;
+
+  return NS_OK;
+}
+
+nsresult
 Database::CheckAndUpdateGUIDs()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // First, import any bookmark guids already set by Sync.
   nsCOMPtr<mozIStorageStatement> updateStmt;
   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
     "UPDATE moz_bookmarks "
--- a/toolkit/components/places/Database.h
+++ b/toolkit/components/places/Database.h
@@ -69,16 +69,18 @@
 #define TOPIC_PLACES_SHUTDOWN "places-shutdown"
 // For Internal use only.  Fired when connection is about to be closed, only
 // cleanup tasks should run at this stage, nothing should be added to the
 // database, nor APIs should be called.
 #define TOPIC_PLACES_WILL_CLOSE_CONNECTION "places-will-close-connection"
 // Fired when the connection has gone, nothing will work from now on.
 #define TOPIC_PLACES_CONNECTION_CLOSED "places-connection-closed"
 
+class nsIStringBundle;
+
 namespace mozilla {
 namespace places {
 
 enum JournalMode {
   // Default SQLite journal mode.
   JOURNAL_DELETE = 0
   // Can reduce fsyncs on Linux when journal is deleted (See bug 460315).
   // We fallback to this mode when WAL is unavailable.
@@ -266,16 +268,21 @@ protected:
    * database.  All migration is done inside a transaction that is rolled back
    * if any error occurs.
    * @param aDatabaseMigrated
    *        Whether a schema upgrade happened.
    */
   nsresult InitSchema(bool* aDatabaseMigrated);
 
   /**
+   * Creates bookmark roots in a new DB.
+   */
+  nsresult CreateBookmarkRoots();
+
+  /**
    * Initializes additionale SQLite functions, defined in SQLFunctions.h
    */
   nsresult InitFunctions();
 
   /**
    * Initializes triggers defined in nsPlacesTriggers.h
    */  
   nsresult InitTempTriggers();
@@ -287,16 +294,17 @@ protected:
   nsresult MigrateV8Up();
   nsresult MigrateV9Up();
   nsresult MigrateV10Up();
   nsresult MigrateV11Up();
   nsresult MigrateV13Up();
   nsresult MigrateV14Up();
   nsresult MigrateV15Up();
 
+  nsresult UpdateBookmarkRootTitles();
   nsresult CheckAndUpdateGUIDs();
 
 private:
   ~Database();
 
   /**
    * Singleton getter, invoked by class instantiation.
    *
--- a/toolkit/components/places/nsNavBookmarks.cpp
+++ b/toolkit/components/places/nsNavBookmarks.cpp
@@ -268,18 +268,17 @@ nsNavBookmarks::Init()
   mRecentBookmarksCache.Init(RECENT_BOOKMARKS_INITIAL_CACHE_SIZE);
   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   if (os) {
     (void)os->AddObserver(this, TOPIC_PLACES_MAINTENANCE, true);
     (void)os->AddObserver(this, TOPIC_PLACES_SHUTDOWN, true);
     (void)os->AddObserver(this, TOPIC_PLACES_CONNECTION_CLOSED, true);
   }
 
-  PRUint16 dbStatus = mDB->GetDatabaseStatus();
-  nsresult rv = InitRoots(dbStatus != nsINavHistoryService::DATABASE_STATUS_OK);
+  nsresult rv = ReadRoots();
   NS_ENSURE_SUCCESS(rv, rv);
 
   mCanNotify = true;
 
   // Observe annotations.
   nsAnnotationService* annosvc = nsAnnotationService::GetAnnotationService();
   NS_ENSURE_TRUE(annosvc, NS_ERROR_OUT_OF_MEMORY);
   annosvc->AddObserver(this);
@@ -292,17 +291,17 @@ nsNavBookmarks::Init()
   history->AddObserver(this, true);
 
   // DO NOT PUT STUFF HERE that can fail. See observer comment above.
 
   return NS_OK;
 }
 
 nsresult
-nsNavBookmarks::InitRoots(bool aForceCreate)
+nsNavBookmarks::ReadRoots()
 {
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = mDB->MainConn()->CreateStatement(NS_LITERAL_CSTRING(
     "SELECT root_name, folder_id FROM moz_bookmarks_roots"
   ), getter_AddRefs(stmt));
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool hasResult;
@@ -327,96 +326,18 @@ nsNavBookmarks::InitRoots(bool aForceCre
     else if (rootName.EqualsLiteral("tags")) {
       mTagsRoot = rootId;
     }
     else if (rootName.EqualsLiteral("unfiled")) {
       mUnfiledRoot = rootId;
     }
   }
 
-  if (aForceCreate) {
-    nsNavHistory* history = nsNavHistory::GetHistoryService();
-    NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
-    nsIStringBundle* bundle = history->GetBundle();
-    NS_ENSURE_TRUE(bundle, NS_ERROR_OUT_OF_MEMORY);
-
-    mozStorageTransaction transaction(mDB->MainConn(), false);
-
-    rv = CreateRoot(NS_LITERAL_CSTRING("places"), &mRoot, 0,
-                    nsnull, nsnull);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = CreateRoot(NS_LITERAL_CSTRING("menu"), &mMenuRoot, mRoot, bundle,
-                    NS_LITERAL_STRING("BookmarksMenuFolderTitle").get());
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = CreateRoot(NS_LITERAL_CSTRING("toolbar"), &mToolbarRoot, mRoot, bundle,
-                    NS_LITERAL_STRING("BookmarksToolbarFolderTitle").get());
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = CreateRoot(NS_LITERAL_CSTRING("tags"), &mTagsRoot, mRoot, bundle,
-                    NS_LITERAL_STRING("TagsFolderTitle").get());
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = CreateRoot(NS_LITERAL_CSTRING("unfiled"), &mUnfiledRoot, mRoot, bundle,
-                    NS_LITERAL_STRING("UnsortedBookmarksFolderTitle").get());
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = transaction.Commit();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    if (!mBatching) {
-      ForceWALCheckpoint();
-    }
-  }
-
-  return NS_OK;
-}
-
-
-nsresult
-nsNavBookmarks::CreateRoot(const nsCString& name,
-                           PRInt64* _itemId,
-                           PRInt64 aParentId,
-                           nsIStringBundle* aBundle,
-                           const PRUnichar* aTitleStringId)
-{
-  nsresult rv;
-
-  if (*_itemId == 0) {
-    // The root does not exist.  Create a new untitled folder for it.
-    rv = CreateFolder(aParentId, EmptyCString(), DEFAULT_INDEX, _itemId);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    // Create a entry  in moz_bookmarks_roots to link the folder to the root.
-    nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
-      "INSERT INTO moz_bookmarks_roots (root_name, folder_id) "
-      "VALUES (:root_name, :item_id)"
-    );
-    NS_ENSURE_STATE(stmt);
-    mozStorageStatementScoper scoper(stmt);
-
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"), name);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), *_itemId);
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = stmt->Execute();
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
-  // Now set the title on the root.  Notice we do this regardless, to take in
-  // could title changes when schema changes.
-  if (aTitleStringId) {
-    nsXPIDLString title;
-    rv = aBundle->GetStringFromName(aTitleStringId, getter_Copies(title));
-    NS_ENSURE_SUCCESS(rv, rv);
-    rv = SetItemTitle(*_itemId, NS_ConvertUTF16toUTF8(title));
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
+  if (!mRoot || !mMenuRoot || !mToolbarRoot || !mTagsRoot || !mUnfiledRoot)
+    return NS_ERROR_FAILURE;
 
   return NS_OK;
 }
 
 // nsNavBookmarks::IsBookmarkedInDatabase
 //
 //    This checks to see if the specified place_id is actually bookmarked.
 
@@ -706,20 +627,16 @@ nsNavBookmarks::InsertBookmark(PRInt64 a
   if (grandParentId != mTagsRoot) {
     rv = history->UpdateFrecency(placeId);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!mBatching) {
-    ForceWALCheckpoint();
-  }
-
   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
                    nsINavBookmarkObserver,
                    OnItemAdded(*aNewBookmarkId, aFolder, index, TYPE_BOOKMARK,
                                aURI, title, dateAdded, guid, folderGuid));
 
   // If the bookmark has been added to a tag container, notify all
   // bookmark-folder result nodes which contain a bookmark for the new
   // bookmark's url.
@@ -810,20 +727,16 @@ nsNavBookmarks::RemoveItem(PRInt64 aItem
                            bookmark.lastModified);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
   END_CRITICAL_BOOKMARK_CACHE_SECTION(bookmark.id);
 
-  if (!mBatching) {
-    ForceWALCheckpoint();
-  }
-
   nsCOMPtr<nsIURI> uri;
   if (bookmark.type == TYPE_BOOKMARK) {
     // If not a tag, recalculate frecency for this entry, since it changed.
     if (bookmark.grandParentId != mTagsRoot) {
       nsNavHistory* history = nsNavHistory::GetHistoryService();
       NS_ENSURE_TRUE(history, NS_ERROR_OUT_OF_MEMORY);
       rv = history->UpdateFrecency(bookmark.placeId);
       NS_ENSURE_SUCCESS(rv, rv);
@@ -970,20 +883,16 @@ nsNavBookmarks::CreateContainerWithID(PR
   rv = InsertBookmarkInDB(-1, FOLDER, aParent, index,
                           title, dateAdded, nsnull, folderGuid, grandParentId,
                           nsnull, aNewFolder, guid);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!mBatching) {
-    ForceWALCheckpoint();
-  }
-
   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
                    nsINavBookmarkObserver,
                    OnItemAdded(*aNewFolder, aParent, index, FOLDER,
                                nsnull, title, dateAdded, guid, folderGuid));
 
   *aIndex = index;
   return NS_OK;
 }
@@ -1293,20 +1202,16 @@ nsNavBookmarks::RemoveFolderChildren(PRI
       NS_ENSURE_SUCCESS(rv, rv);
     }
     END_CRITICAL_BOOKMARK_CACHE_SECTION(child.id);
   }
 
   rv = transaction.Commit();
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!mBatching) {
-    ForceWALCheckpoint();
-  }
-
   // Call observers in reverse order to serve children before their parent.
   for (PRInt32 i = folderChildrenArray.Length() - 1; i >= 0; --i) {
     BookmarkData& child = folderChildrenArray[i];
     nsCOMPtr<nsIURI> uri;
     if (child.type == TYPE_BOOKMARK) {
       // A broken url should not interrupt the removal process.
       (void)NS_NewURI(getter_AddRefs(uri), child.url);
     }
@@ -2950,17 +2855,16 @@ nsNavBookmarks::OnBeginUpdateBatch()
 }
 
 
 NS_IMETHODIMP
 nsNavBookmarks::OnEndUpdateBatch()
 {
   if (mBatching) {
     mBatching = false;
-    ForceWALCheckpoint();
   }
 
   NOTIFY_OBSERVERS(mCanNotify, mCacheObservers, mObservers,
                    nsINavBookmarkObserver, OnEndUpdateBatch());
   return NS_OK;
 }
 
 
--- a/toolkit/components/places/nsNavBookmarks.h
+++ b/toolkit/components/places/nsNavBookmarks.h
@@ -258,45 +258,18 @@ public:
 private:
   static nsNavBookmarks* gBookmarksService;
 
   ~nsNavBookmarks();
 
   /**
    * Locates the root items in the bookmarks folder hierarchy assigning folder
    * ids to the root properties that are exposed through the service interface.
-   * 
-   * @param aForceCreate
-   *        Whether the method should try creating the roots.  It should be set
-   *        to true if the database has just been created or upgraded.
-   *
-   * @note The creation of roots skips already existing entries.
    */
-  nsresult InitRoots(bool aForceCreate);
-
-  /**
-   * Tries to create a root folder with the given name.
-   *
-   * @param name
-   *        Name associated to the root.
-   * @param _itemId
-   *        if set CreateRoot will skip creation, otherwise will return the
-   *        newly created folder id.
-   * @param aParentId
-   *        Id of the parent that should cotain this root.
-   * @param aBundle
-   *        Stringbundle used to get the visible title of the root.
-   * @param aTitleStringId
-   *        Id of the title string in the stringbundle.
-   */
-  nsresult CreateRoot(const nsCString& name,
-                      PRInt64* _itemId,
-                      PRInt64 aParentId,
-                      nsIStringBundle* aBundle,
-                      const PRUnichar* aTitleStringId);
+  nsresult ReadRoots();
 
   nsresult AdjustIndices(PRInt64 aFolder,
                          PRInt32 aStartIndex,
                          PRInt32 aEndIndex,
                          PRInt32 aDelta);
 
   /**
    * Fetches properties of a folder.
--- a/toolkit/components/places/tests/unit/test_analyze.js
+++ b/toolkit/components/places/tests/unit/test_analyze.js
@@ -1,27 +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, but since
-// the tables are empty it won't contain any data.
+// Tests sqlite_sta1 table exists, it should be created by analyze.
+// Since the bookmark roots are created when the DB is created (bug 704855),
+// the table will contain data.
 
 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_false(this._gotResult);
+      do_check_true(this._gotResult);
        do_test_finished();
     }
   });
   stmt.finalize();
 }