toolkit/components/places/Database.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/toolkit/components/places/Database.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,2039 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include "mozilla/ArrayUtils.h"
     1.9 +#include "mozilla/Attributes.h"
    1.10 +#include "mozilla/DebugOnly.h"
    1.11 +
    1.12 +#include "Database.h"
    1.13 +
    1.14 +#include "nsINavBookmarksService.h"
    1.15 +#include "nsIInterfaceRequestorUtils.h"
    1.16 +#include "nsIFile.h"
    1.17 +
    1.18 +#include "nsNavHistory.h"
    1.19 +#include "nsPlacesTables.h"
    1.20 +#include "nsPlacesIndexes.h"
    1.21 +#include "nsPlacesTriggers.h"
    1.22 +#include "nsPlacesMacros.h"
    1.23 +#include "SQLFunctions.h"
    1.24 +#include "Helpers.h"
    1.25 +
    1.26 +#include "nsAppDirectoryServiceDefs.h"
    1.27 +#include "nsDirectoryServiceUtils.h"
    1.28 +#include "prsystem.h"
    1.29 +#include "nsPrintfCString.h"
    1.30 +#include "mozilla/Preferences.h"
    1.31 +#include "mozilla/Services.h"
    1.32 +#include "prtime.h"
    1.33 +
    1.34 +// Time between corrupt database backups.
    1.35 +#define RECENT_BACKUP_TIME_MICROSEC (int64_t)86400 * PR_USEC_PER_SEC // 24H
    1.36 +
    1.37 +// Filename of the database.
    1.38 +#define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite")
    1.39 +// Filename used to backup corrupt databases.
    1.40 +#define DATABASE_CORRUPT_FILENAME NS_LITERAL_STRING("places.sqlite.corrupt")
    1.41 +
    1.42 +// Set when the database file was found corrupt by a previous maintenance.
    1.43 +#define PREF_FORCE_DATABASE_REPLACEMENT "places.database.replaceOnStartup"
    1.44 +
    1.45 +// Set to specify the size of the places database growth increments in kibibytes
    1.46 +#define PREF_GROWTH_INCREMENT_KIB "places.database.growthIncrementKiB"
    1.47 +
    1.48 +// Maximum size for the WAL file.  It should be small enough since in case of
    1.49 +// crashes we could lose all the transactions in the file.  But a too small
    1.50 +// file could hurt performance.
    1.51 +#define DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES 512
    1.52 +
    1.53 +#define BYTES_PER_KIBIBYTE 1024
    1.54 +
    1.55 +// Old Sync GUID annotation.
    1.56 +#define SYNCGUID_ANNO NS_LITERAL_CSTRING("sync/guid")
    1.57 +
    1.58 +// Places string bundle, contains internationalized bookmark root names.
    1.59 +#define PLACES_BUNDLE "chrome://places/locale/places.properties"
    1.60 +
    1.61 +// Livemarks annotations.
    1.62 +#define LMANNO_FEEDURI "livemark/feedURI"
    1.63 +#define LMANNO_SITEURI "livemark/siteURI"
    1.64 +
    1.65 +using namespace mozilla;
    1.66 +
    1.67 +namespace mozilla {
    1.68 +namespace places {
    1.69 +
    1.70 +namespace {
    1.71 +
    1.72 +////////////////////////////////////////////////////////////////////////////////
    1.73 +//// Helpers
    1.74 +
    1.75 +/**
    1.76 + * Checks whether exists a database backup created not longer than
    1.77 + * RECENT_BACKUP_TIME_MICROSEC ago.
    1.78 + */
    1.79 +bool
    1.80 +hasRecentCorruptDB()
    1.81 +{
    1.82 +  MOZ_ASSERT(NS_IsMainThread());
    1.83 +
    1.84 +  nsCOMPtr<nsIFile> profDir;
    1.85 +  NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profDir));
    1.86 +  NS_ENSURE_TRUE(profDir, false);
    1.87 +  nsCOMPtr<nsISimpleEnumerator> entries;
    1.88 +  profDir->GetDirectoryEntries(getter_AddRefs(entries));
    1.89 +  NS_ENSURE_TRUE(entries, false);
    1.90 +  bool hasMore;
    1.91 +  while (NS_SUCCEEDED(entries->HasMoreElements(&hasMore)) && hasMore) {
    1.92 +    nsCOMPtr<nsISupports> next;
    1.93 +    entries->GetNext(getter_AddRefs(next));
    1.94 +    NS_ENSURE_TRUE(next, false);
    1.95 +    nsCOMPtr<nsIFile> currFile = do_QueryInterface(next);
    1.96 +    NS_ENSURE_TRUE(currFile, false);
    1.97 +
    1.98 +    nsAutoString leafName;
    1.99 +    if (NS_SUCCEEDED(currFile->GetLeafName(leafName)) &&
   1.100 +        leafName.Length() >= DATABASE_CORRUPT_FILENAME.Length() &&
   1.101 +        leafName.Find(".corrupt", DATABASE_FILENAME.Length()) != -1) {
   1.102 +      PRTime lastMod = 0;
   1.103 +      currFile->GetLastModifiedTime(&lastMod);
   1.104 +      NS_ENSURE_TRUE(lastMod > 0, false);
   1.105 +      return (PR_Now() - lastMod) > RECENT_BACKUP_TIME_MICROSEC;
   1.106 +    }
   1.107 +  }
   1.108 +  return false;
   1.109 +}
   1.110 +
   1.111 +/**
   1.112 + * Updates sqlite_stat1 table through ANALYZE.
   1.113 + * Since also nsPlacesExpiration.js executes ANALYZE, the analyzed tables
   1.114 + * must be the same in both components.  So ensure they are in sync.
   1.115 + *
   1.116 + * @param aDBConn
   1.117 + *        The database connection.
   1.118 + */
   1.119 +nsresult
   1.120 +updateSQLiteStatistics(mozIStorageConnection* aDBConn)
   1.121 +{
   1.122 +  MOZ_ASSERT(NS_IsMainThread());
   1.123 +  nsCOMPtr<mozIStorageAsyncStatement> analyzePlacesStmt;
   1.124 +  aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.125 +    "ANALYZE moz_places"
   1.126 +  ), getter_AddRefs(analyzePlacesStmt));
   1.127 +  NS_ENSURE_STATE(analyzePlacesStmt);
   1.128 +  nsCOMPtr<mozIStorageAsyncStatement> analyzeBookmarksStmt;
   1.129 +  aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.130 +    "ANALYZE moz_bookmarks"
   1.131 +  ), getter_AddRefs(analyzeBookmarksStmt));
   1.132 +  NS_ENSURE_STATE(analyzeBookmarksStmt);
   1.133 +  nsCOMPtr<mozIStorageAsyncStatement> analyzeVisitsStmt;
   1.134 +  aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.135 +    "ANALYZE moz_historyvisits"
   1.136 +  ), getter_AddRefs(analyzeVisitsStmt));
   1.137 +  NS_ENSURE_STATE(analyzeVisitsStmt);
   1.138 +  nsCOMPtr<mozIStorageAsyncStatement> analyzeInputStmt;
   1.139 +  aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.140 +    "ANALYZE moz_inputhistory"
   1.141 +  ), getter_AddRefs(analyzeInputStmt));
   1.142 +  NS_ENSURE_STATE(analyzeInputStmt);
   1.143 +
   1.144 +  mozIStorageBaseStatement *stmts[] = {
   1.145 +    analyzePlacesStmt,
   1.146 +    analyzeBookmarksStmt,
   1.147 +    analyzeVisitsStmt,
   1.148 +    analyzeInputStmt
   1.149 +  };
   1.150 +
   1.151 +  nsCOMPtr<mozIStoragePendingStatement> ps;
   1.152 +  (void)aDBConn->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
   1.153 +                              getter_AddRefs(ps));
   1.154 +  return NS_OK;
   1.155 +}
   1.156 +
   1.157 +/**
   1.158 + * Sets the connection journal mode to one of the JOURNAL_* types.
   1.159 + *
   1.160 + * @param aDBConn
   1.161 + *        The database connection.
   1.162 + * @param aJournalMode
   1.163 + *        One of the JOURNAL_* types.
   1.164 + * @returns the current journal mode.
   1.165 + * @note this may return a different journal mode than the required one, since
   1.166 + *       setting it may fail.
   1.167 + */
   1.168 +enum JournalMode
   1.169 +SetJournalMode(nsCOMPtr<mozIStorageConnection>& aDBConn,
   1.170 +                             enum JournalMode aJournalMode)
   1.171 +{
   1.172 +  MOZ_ASSERT(NS_IsMainThread());
   1.173 +  nsAutoCString journalMode;
   1.174 +  switch (aJournalMode) {
   1.175 +    default:
   1.176 +      MOZ_ASSERT(false, "Trying to set an unknown journal mode.");
   1.177 +      // Fall through to the default DELETE journal.
   1.178 +    case JOURNAL_DELETE:
   1.179 +      journalMode.AssignLiteral("delete");
   1.180 +      break;
   1.181 +    case JOURNAL_TRUNCATE:
   1.182 +      journalMode.AssignLiteral("truncate");
   1.183 +      break;
   1.184 +    case JOURNAL_MEMORY:
   1.185 +      journalMode.AssignLiteral("memory");
   1.186 +      break;
   1.187 +    case JOURNAL_WAL:
   1.188 +      journalMode.AssignLiteral("wal");
   1.189 +      break;
   1.190 +  }
   1.191 +
   1.192 +  nsCOMPtr<mozIStorageStatement> statement;
   1.193 +  nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR
   1.194 +		      "PRAGMA journal_mode = ");
   1.195 +  query.Append(journalMode);
   1.196 +  aDBConn->CreateStatement(query, getter_AddRefs(statement));
   1.197 +  NS_ENSURE_TRUE(statement, JOURNAL_DELETE);
   1.198 +
   1.199 +  bool hasResult = false;
   1.200 +  if (NS_SUCCEEDED(statement->ExecuteStep(&hasResult)) && hasResult &&
   1.201 +      NS_SUCCEEDED(statement->GetUTF8String(0, journalMode))) {
   1.202 +    if (journalMode.EqualsLiteral("delete")) {
   1.203 +      return JOURNAL_DELETE;
   1.204 +    }
   1.205 +    if (journalMode.EqualsLiteral("truncate")) {
   1.206 +      return JOURNAL_TRUNCATE;
   1.207 +    }
   1.208 +    if (journalMode.EqualsLiteral("memory")) {
   1.209 +      return JOURNAL_MEMORY;
   1.210 +    }
   1.211 +    if (journalMode.EqualsLiteral("wal")) {
   1.212 +      return JOURNAL_WAL;
   1.213 +    }
   1.214 +    // This is an unknown journal.
   1.215 +    MOZ_ASSERT(true);
   1.216 +  }
   1.217 +
   1.218 +  return JOURNAL_DELETE;
   1.219 +}
   1.220 +
   1.221 +class ConnectionCloseCallback MOZ_FINAL : public mozIStorageCompletionCallback {
   1.222 +  bool mDone;
   1.223 +
   1.224 +public:
   1.225 +  NS_DECL_THREADSAFE_ISUPPORTS
   1.226 +  NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
   1.227 +  ConnectionCloseCallback();
   1.228 +};
   1.229 +
   1.230 +NS_IMETHODIMP
   1.231 +ConnectionCloseCallback::Complete(nsresult, nsISupports*)
   1.232 +{
   1.233 +  mDone = true;
   1.234 +  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.235 +  MOZ_ASSERT(os);
   1.236 +  if (!os)
   1.237 +    return NS_OK;
   1.238 +  DebugOnly<nsresult> rv = os->NotifyObservers(nullptr,
   1.239 +                                               TOPIC_PLACES_CONNECTION_CLOSED,
   1.240 +                                               nullptr);
   1.241 +  MOZ_ASSERT(NS_SUCCEEDED(rv));
   1.242 +  return NS_OK;
   1.243 +}
   1.244 +
   1.245 +ConnectionCloseCallback::ConnectionCloseCallback()
   1.246 +  : mDone(false)
   1.247 +{
   1.248 +  MOZ_ASSERT(NS_IsMainThread());
   1.249 +}
   1.250 +
   1.251 +NS_IMPL_ISUPPORTS(
   1.252 +  ConnectionCloseCallback
   1.253 +, mozIStorageCompletionCallback
   1.254 +)
   1.255 +
   1.256 +nsresult
   1.257 +CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
   1.258 +           const nsCString& aRootName,
   1.259 +           const nsXPIDLString& titleString)
   1.260 +{
   1.261 +  MOZ_ASSERT(NS_IsMainThread());
   1.262 +
   1.263 +  // The position of the new item in its folder.
   1.264 +  static int32_t itemPosition = 0;
   1.265 +
   1.266 +  // A single creation timestamp for all roots so that the root folder's
   1.267 +  // last modification time isn't earlier than its childrens' creation time.
   1.268 +  static PRTime timestamp = 0;
   1.269 +  if (!timestamp)
   1.270 +    timestamp = PR_Now();
   1.271 +
   1.272 +  // Create a new bookmark folder for the root.
   1.273 +  nsCOMPtr<mozIStorageStatement> stmt;
   1.274 +  nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
   1.275 +    "INSERT INTO moz_bookmarks "
   1.276 +      "(type, position, title, dateAdded, lastModified, guid, parent) "
   1.277 +    "VALUES (:item_type, :item_position, :item_title,"
   1.278 +            ":date_added, :last_modified, GENERATE_GUID(),"
   1.279 +            "IFNULL((SELECT id FROM moz_bookmarks WHERE parent = 0), 0))"
   1.280 +  ), getter_AddRefs(stmt));
   1.281 +  if (NS_FAILED(rv)) return rv;
   1.282 +
   1.283 +  rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"),
   1.284 +                             nsINavBookmarksService::TYPE_FOLDER);
   1.285 +  if (NS_FAILED(rv)) return rv;
   1.286 +  rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"), itemPosition);
   1.287 +  if (NS_FAILED(rv)) return rv;
   1.288 +  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
   1.289 +                                  NS_ConvertUTF16toUTF8(titleString));
   1.290 +  if (NS_FAILED(rv)) return rv;
   1.291 +  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), timestamp);
   1.292 +  if (NS_FAILED(rv)) return rv;
   1.293 +  rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), timestamp);
   1.294 +  if (NS_FAILED(rv)) return rv;
   1.295 +  rv = stmt->Execute();
   1.296 +  if (NS_FAILED(rv)) return rv;
   1.297 +
   1.298 +  // Create an entry in moz_bookmarks_roots to link the folder to the root.
   1.299 +  nsCOMPtr<mozIStorageStatement> newRootStmt;
   1.300 +  rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
   1.301 +    "INSERT INTO moz_bookmarks_roots (root_name, folder_id) "
   1.302 +    "VALUES (:root_name, "
   1.303 +              "(SELECT id from moz_bookmarks WHERE "
   1.304 +              " position = :item_position AND "
   1.305 +              " parent = IFNULL((SELECT MIN(folder_id) FROM moz_bookmarks_roots), 0)))"
   1.306 +  ), getter_AddRefs(newRootStmt));
   1.307 +  if (NS_FAILED(rv)) return rv;
   1.308 +
   1.309 +  rv = newRootStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"),
   1.310 +                                         aRootName);
   1.311 +  if (NS_FAILED(rv)) return rv;
   1.312 +  rv = newRootStmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"),
   1.313 +                                    itemPosition);
   1.314 +  if (NS_FAILED(rv)) return rv;
   1.315 +  rv = newRootStmt->Execute();
   1.316 +  if (NS_FAILED(rv)) return rv;
   1.317 +
   1.318 +  // The 'places' root is a folder containing the other roots.
   1.319 +  // The first bookmark in a folder has position 0.
   1.320 +  if (!aRootName.Equals("places"))
   1.321 +    ++itemPosition;
   1.322 +
   1.323 +  return NS_OK;
   1.324 +}
   1.325 +
   1.326 +
   1.327 +} // Anonymous namespace
   1.328 +
   1.329 +////////////////////////////////////////////////////////////////////////////////
   1.330 +//// Database
   1.331 +
   1.332 +PLACES_FACTORY_SINGLETON_IMPLEMENTATION(Database, gDatabase)
   1.333 +
   1.334 +NS_IMPL_ISUPPORTS(Database
   1.335 +, nsIObserver
   1.336 +, nsISupportsWeakReference
   1.337 +)
   1.338 +
   1.339 +Database::Database()
   1.340 +  : mMainThreadStatements(mMainConn)
   1.341 +  , mMainThreadAsyncStatements(mMainConn)
   1.342 +  , mAsyncThreadStatements(mMainConn)
   1.343 +  , mDBPageSize(0)
   1.344 +  , mDatabaseStatus(nsINavHistoryService::DATABASE_STATUS_OK)
   1.345 +  , mShuttingDown(false)
   1.346 +  , mClosed(false)
   1.347 +{
   1.348 +  // Attempting to create two instances of the service?
   1.349 +  MOZ_ASSERT(!gDatabase);
   1.350 +  gDatabase = this;
   1.351 +}
   1.352 +
   1.353 +Database::~Database()
   1.354 +{
   1.355 +  // Check to make sure it's us, in case somebody wrongly creates an extra
   1.356 +  // instance of this singleton class.
   1.357 +  MOZ_ASSERT(gDatabase == this);
   1.358 +
   1.359 +  // Remove the static reference to the service.
   1.360 +  if (gDatabase == this) {
   1.361 +    gDatabase = nullptr;
   1.362 +  }
   1.363 +}
   1.364 +
   1.365 +nsresult
   1.366 +Database::Init()
   1.367 +{
   1.368 +  MOZ_ASSERT(NS_IsMainThread());
   1.369 +
   1.370 +  nsCOMPtr<mozIStorageService> storage =
   1.371 +    do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   1.372 +  NS_ENSURE_STATE(storage);
   1.373 +
   1.374 +  // Init the database file and connect to it.
   1.375 +  bool databaseCreated = false;
   1.376 +  nsresult rv = InitDatabaseFile(storage, &databaseCreated);
   1.377 +  if (NS_SUCCEEDED(rv) && databaseCreated) {
   1.378 +    mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CREATE;
   1.379 +  }
   1.380 +  else if (rv == NS_ERROR_FILE_CORRUPTED) {
   1.381 +    // The database is corrupt, backup and replace it with a new one.
   1.382 +    mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CORRUPT;
   1.383 +    rv = BackupAndReplaceDatabaseFile(storage);
   1.384 +    // Fallback to catch-all handler, that notifies a database locked failure.
   1.385 +  }
   1.386 +
   1.387 +  // If the database connection still cannot be opened, it may just be locked
   1.388 +  // by third parties.  Send out a notification and interrupt initialization.
   1.389 +  if (NS_FAILED(rv)) {
   1.390 +    nsRefPtr<PlacesEvent> lockedEvent = new PlacesEvent(TOPIC_DATABASE_LOCKED);
   1.391 +    (void)NS_DispatchToMainThread(lockedEvent);
   1.392 +    return rv;
   1.393 +  }
   1.394 +
   1.395 +  // Initialize the database schema.  In case of failure the existing schema is
   1.396 +  // is corrupt or incoherent, thus the database should be replaced.
   1.397 +  bool databaseMigrated = false;
   1.398 +  rv = InitSchema(&databaseMigrated);
   1.399 +  if (NS_FAILED(rv)) {
   1.400 +    mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CORRUPT;
   1.401 +    rv = BackupAndReplaceDatabaseFile(storage);
   1.402 +    NS_ENSURE_SUCCESS(rv, rv);
   1.403 +    // Try to initialize the schema again on the new database.
   1.404 +    rv = InitSchema(&databaseMigrated);
   1.405 +    NS_ENSURE_SUCCESS(rv, rv);
   1.406 +  }
   1.407 +
   1.408 +  if (databaseMigrated) {
   1.409 +    mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_UPGRADED;
   1.410 +  }
   1.411 +
   1.412 +  if (mDatabaseStatus != nsINavHistoryService::DATABASE_STATUS_OK) {
   1.413 +    rv = updateSQLiteStatistics(MainConn());
   1.414 +    NS_ENSURE_SUCCESS(rv, rv);
   1.415 +  }
   1.416 +
   1.417 +  // Initialize here all the items that are not part of the on-disk database,
   1.418 +  // like views, temp triggers or temp tables.  The database should not be
   1.419 +  // considered corrupt if any of the following fails.
   1.420 +
   1.421 +  rv = InitTempTriggers();
   1.422 +  NS_ENSURE_SUCCESS(rv, rv);
   1.423 +
   1.424 +  // Notify we have finished database initialization.
   1.425 +  // Enqueue the notification, so if we init another service that requires
   1.426 +  // nsNavHistoryService we don't recursive try to get it.
   1.427 +  nsRefPtr<PlacesEvent> completeEvent =
   1.428 +    new PlacesEvent(TOPIC_PLACES_INIT_COMPLETE);
   1.429 +  rv = NS_DispatchToMainThread(completeEvent);
   1.430 +  NS_ENSURE_SUCCESS(rv, rv);
   1.431 +
   1.432 +  // Finally observe profile shutdown notifications.
   1.433 +  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.434 +  if (os) {
   1.435 +    (void)os->AddObserver(this, TOPIC_PROFILE_CHANGE_TEARDOWN, true);
   1.436 +    (void)os->AddObserver(this, TOPIC_PROFILE_BEFORE_CHANGE, true);
   1.437 +  }
   1.438 +
   1.439 +  return NS_OK;
   1.440 +}
   1.441 +
   1.442 +nsresult
   1.443 +Database::InitDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage,
   1.444 +                           bool* aNewDatabaseCreated)
   1.445 +{
   1.446 +  MOZ_ASSERT(NS_IsMainThread());
   1.447 +  *aNewDatabaseCreated = false;
   1.448 +
   1.449 +  nsCOMPtr<nsIFile> databaseFile;
   1.450 +  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   1.451 +                                       getter_AddRefs(databaseFile));
   1.452 +  NS_ENSURE_SUCCESS(rv, rv);
   1.453 +  rv = databaseFile->Append(DATABASE_FILENAME);
   1.454 +  NS_ENSURE_SUCCESS(rv, rv);
   1.455 +
   1.456 +  bool databaseFileExists = false;
   1.457 +  rv = databaseFile->Exists(&databaseFileExists);
   1.458 +  NS_ENSURE_SUCCESS(rv, rv);
   1.459 +
   1.460 +  if (databaseFileExists &&
   1.461 +      Preferences::GetBool(PREF_FORCE_DATABASE_REPLACEMENT, false)) {
   1.462 +    // If this pref is set, Maintenance required a database replacement, due to
   1.463 +    // integrity corruption.
   1.464 +    // Be sure to clear the pref to avoid handling it more than once.
   1.465 +    (void)Preferences::ClearUser(PREF_FORCE_DATABASE_REPLACEMENT);
   1.466 +
   1.467 +    return NS_ERROR_FILE_CORRUPTED;
   1.468 +  }
   1.469 +
   1.470 +  // Open the database file.  If it does not exist a new one will be created.
   1.471 +  // Use an unshared connection, it will consume more memory but avoid shared
   1.472 +  // cache contentions across threads.
   1.473 +  rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
   1.474 +  NS_ENSURE_SUCCESS(rv, rv);
   1.475 +
   1.476 +  *aNewDatabaseCreated = !databaseFileExists;
   1.477 +  return NS_OK;
   1.478 +}
   1.479 +
   1.480 +nsresult
   1.481 +Database::BackupAndReplaceDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage)
   1.482 +{
   1.483 +  MOZ_ASSERT(NS_IsMainThread());
   1.484 +  nsCOMPtr<nsIFile> profDir;
   1.485 +  nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
   1.486 +                                       getter_AddRefs(profDir));
   1.487 +  NS_ENSURE_SUCCESS(rv, rv);
   1.488 +  nsCOMPtr<nsIFile> databaseFile;
   1.489 +  rv = profDir->Clone(getter_AddRefs(databaseFile));
   1.490 +  NS_ENSURE_SUCCESS(rv, rv);
   1.491 +  rv = databaseFile->Append(DATABASE_FILENAME);
   1.492 +  NS_ENSURE_SUCCESS(rv, rv);
   1.493 +
   1.494 +  // If we have
   1.495 +  // already failed in the last 24 hours avoid to create another corrupt file,
   1.496 +  // since doing so, in some situation, could cause us to create a new corrupt
   1.497 +  // file at every try to access any Places service.  That is bad because it
   1.498 +  // would quickly fill the user's disk space without any notice.
   1.499 +  if (!hasRecentCorruptDB()) {
   1.500 +    nsCOMPtr<nsIFile> backup;
   1.501 +    (void)aStorage->BackupDatabaseFile(databaseFile, DATABASE_CORRUPT_FILENAME,
   1.502 +                                       profDir, getter_AddRefs(backup));
   1.503 +  }
   1.504 +
   1.505 +  // Close database connection if open.
   1.506 +  if (mMainConn) {
   1.507 +    // If there's any not finalized statement or this fails for any reason
   1.508 +    // we won't be able to remove the database.
   1.509 +    rv = mMainConn->Close();
   1.510 +    NS_ENSURE_SUCCESS(rv, rv);
   1.511 +  }
   1.512 +
   1.513 +  // Remove the broken database.
   1.514 +  rv = databaseFile->Remove(false);
   1.515 +  NS_ENSURE_SUCCESS(rv, rv);
   1.516 +
   1.517 +  // Create a new database file.
   1.518 +  // Use an unshared connection, it will consume more memory but avoid shared
   1.519 +  // cache contentions across threads.
   1.520 +  rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
   1.521 +  NS_ENSURE_SUCCESS(rv, rv);
   1.522 +
   1.523 +  return NS_OK;
   1.524 +}
   1.525 +
   1.526 +nsresult
   1.527 +Database::InitSchema(bool* aDatabaseMigrated)
   1.528 +{
   1.529 +  MOZ_ASSERT(NS_IsMainThread());
   1.530 +  *aDatabaseMigrated = false;
   1.531 +
   1.532 +  // WARNING: any statement executed before setting the journal mode must be
   1.533 +  // finalized, since SQLite doesn't allow changing the journal mode if there
   1.534 +  // is any outstanding statement.
   1.535 +
   1.536 +  {
   1.537 +    // Get the page size.  This may be different than the default if the
   1.538 +    // database file already existed with a different page size.
   1.539 +    nsCOMPtr<mozIStorageStatement> statement;
   1.540 +    nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
   1.541 +      MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"
   1.542 +    ), getter_AddRefs(statement));
   1.543 +    NS_ENSURE_SUCCESS(rv, rv);
   1.544 +    bool hasResult = false;
   1.545 +    rv = statement->ExecuteStep(&hasResult);
   1.546 +    NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_FAILURE);
   1.547 +    rv = statement->GetInt32(0, &mDBPageSize);
   1.548 +    NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && mDBPageSize > 0, NS_ERROR_UNEXPECTED);
   1.549 +  }
   1.550 +
   1.551 +  // Ensure that temp tables are held in memory, not on disk.
   1.552 +  nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.553 +      MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA temp_store = MEMORY"));
   1.554 +  NS_ENSURE_SUCCESS(rv, rv);
   1.555 +
   1.556 +  // Be sure to set journal mode after page_size.  WAL would prevent the change
   1.557 +  // otherwise.
   1.558 +  if (JOURNAL_WAL == SetJournalMode(mMainConn, JOURNAL_WAL)) {
   1.559 +    // Set the WAL journal size limit.  We want it to be small, since in
   1.560 +    // synchronous = NORMAL mode a crash could cause loss of all the
   1.561 +    // transactions in the journal.  For added safety we will also force
   1.562 +    // checkpointing at strategic moments.
   1.563 +    int32_t checkpointPages =
   1.564 +      static_cast<int32_t>(DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES * 1024 / mDBPageSize);
   1.565 +    nsAutoCString checkpointPragma("PRAGMA wal_autocheckpoint = ");
   1.566 +    checkpointPragma.AppendInt(checkpointPages);
   1.567 +    rv = mMainConn->ExecuteSimpleSQL(checkpointPragma);
   1.568 +    NS_ENSURE_SUCCESS(rv, rv);
   1.569 +  }
   1.570 +  else {
   1.571 +    // Ignore errors, if we fail here the database could be considered corrupt
   1.572 +    // and we won't be able to go on, even if it's just matter of a bogus file
   1.573 +    // system.  The default mode (DELETE) will be fine in such a case.
   1.574 +    (void)SetJournalMode(mMainConn, JOURNAL_TRUNCATE);
   1.575 +
   1.576 +    // Set synchronous to FULL to ensure maximum data integrity, even in
   1.577 +    // case of crashes or unclean shutdowns.
   1.578 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
   1.579 +        "PRAGMA synchronous = FULL"));
   1.580 +    NS_ENSURE_SUCCESS(rv, rv);
   1.581 +  }
   1.582 +
   1.583 +  // The journal is usually free to grow for performance reasons, but it never
   1.584 +  // shrinks back.  Since the space taken may be problematic, especially on
   1.585 +  // mobile devices, limit its size.
   1.586 +  // Since exceeding the limit will cause a truncate, allow a slightly
   1.587 +  // larger limit than DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES to reduce the number
   1.588 +  // of times it is needed.
   1.589 +  nsAutoCString journalSizePragma("PRAGMA journal_size_limit = ");
   1.590 +  journalSizePragma.AppendInt(DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES * 3);
   1.591 +  (void)mMainConn->ExecuteSimpleSQL(journalSizePragma);
   1.592 +
   1.593 +  // Grow places in |growthIncrementKiB| increments to limit fragmentation on disk.
   1.594 +  // By default, it's 10 MB.
   1.595 +  int32_t growthIncrementKiB =
   1.596 +    Preferences::GetInt(PREF_GROWTH_INCREMENT_KIB, 10 * BYTES_PER_KIBIBYTE);
   1.597 +  if (growthIncrementKiB > 0) {
   1.598 +    (void)mMainConn->SetGrowthIncrement(growthIncrementKiB * BYTES_PER_KIBIBYTE, EmptyCString());
   1.599 +  }
   1.600 +
   1.601 +  // We use our functions during migration, so initialize them now.
   1.602 +  rv = InitFunctions();
   1.603 +  NS_ENSURE_SUCCESS(rv, rv);
   1.604 +
   1.605 +  // Get the database schema version.
   1.606 +  int32_t currentSchemaVersion;
   1.607 +  rv = mMainConn->GetSchemaVersion(&currentSchemaVersion);
   1.608 +  NS_ENSURE_SUCCESS(rv, rv);
   1.609 +  bool databaseInitialized = currentSchemaVersion > 0;
   1.610 +
   1.611 +  if (databaseInitialized && currentSchemaVersion == DATABASE_SCHEMA_VERSION) {
   1.612 +    // The database is up to date and ready to go.
   1.613 +    return NS_OK;
   1.614 +  }
   1.615 +
   1.616 +  // We are going to update the database, so everything from now on should be in
   1.617 +  // a transaction for performances.
   1.618 +  mozStorageTransaction transaction(mMainConn, false);
   1.619 +
   1.620 +  if (databaseInitialized) {
   1.621 +    // Migration How-to:
   1.622 +    //
   1.623 +    // 1. increment PLACES_SCHEMA_VERSION.
   1.624 +    // 2. implement a method that performs upgrade to your version from the
   1.625 +    //    previous one.
   1.626 +    //
   1.627 +    // NOTE: The downgrade process is pretty much complicated by the fact old
   1.628 +    //       versions cannot know what a new version is going to implement.
   1.629 +    //       The only thing we will do for downgrades is setting back the schema
   1.630 +    //       version, so that next upgrades will run again the migration step.
   1.631 +
   1.632 +    if (currentSchemaVersion < DATABASE_SCHEMA_VERSION) {
   1.633 +      *aDatabaseMigrated = true;
   1.634 +
   1.635 +      if (currentSchemaVersion < 6) {
   1.636 +        // These are early Firefox 3.0 alpha versions that are not supported
   1.637 +        // anymore.  In this case it's safer to just replace the database.
   1.638 +        return NS_ERROR_FILE_CORRUPTED;
   1.639 +      }
   1.640 +
   1.641 +      // Firefox 3.0 uses schema version 6.
   1.642 +
   1.643 +      if (currentSchemaVersion < 7) {
   1.644 +        rv = MigrateV7Up();
   1.645 +        NS_ENSURE_SUCCESS(rv, rv);
   1.646 +      }
   1.647 +
   1.648 +      if (currentSchemaVersion < 8) {
   1.649 +        rv = MigrateV8Up();
   1.650 +        NS_ENSURE_SUCCESS(rv, rv);
   1.651 +      }
   1.652 +
   1.653 +      // Firefox 3.5 uses schema version 8.
   1.654 +
   1.655 +      if (currentSchemaVersion < 9) {
   1.656 +        rv = MigrateV9Up();
   1.657 +        NS_ENSURE_SUCCESS(rv, rv);
   1.658 +      }
   1.659 +
   1.660 +      if (currentSchemaVersion < 10) {
   1.661 +        rv = MigrateV10Up();
   1.662 +        NS_ENSURE_SUCCESS(rv, rv);
   1.663 +      }
   1.664 +
   1.665 +      // Firefox 3.6 uses schema version 10.
   1.666 +
   1.667 +      if (currentSchemaVersion < 11) {
   1.668 +        rv = MigrateV11Up();
   1.669 +        NS_ENSURE_SUCCESS(rv, rv);
   1.670 +      }
   1.671 +
   1.672 +      // Firefox 4 uses schema version 11.
   1.673 +
   1.674 +      // Firefox 8 uses schema version 12.
   1.675 +
   1.676 +      if (currentSchemaVersion < 13) {
   1.677 +        rv = MigrateV13Up();
   1.678 +        NS_ENSURE_SUCCESS(rv, rv);
   1.679 +      }
   1.680 +
   1.681 +      if (currentSchemaVersion < 14) {
   1.682 +        rv = MigrateV14Up();
   1.683 +        NS_ENSURE_SUCCESS(rv, rv);
   1.684 +      }
   1.685 +
   1.686 +      if (currentSchemaVersion < 15) {
   1.687 +        rv = MigrateV15Up();
   1.688 +        NS_ENSURE_SUCCESS(rv, rv);
   1.689 +      }
   1.690 +
   1.691 +      if (currentSchemaVersion < 16) {
   1.692 +        rv = MigrateV16Up();
   1.693 +        NS_ENSURE_SUCCESS(rv, rv);
   1.694 +      }
   1.695 +
   1.696 +      // Firefox 11 uses schema version 16.
   1.697 +
   1.698 +      if (currentSchemaVersion < 17) {
   1.699 +        rv = MigrateV17Up();
   1.700 +        NS_ENSURE_SUCCESS(rv, rv);
   1.701 +      }
   1.702 +
   1.703 +      // Firefox 12 uses schema version 17.
   1.704 +
   1.705 +      if (currentSchemaVersion < 18) {
   1.706 +        rv = MigrateV18Up();
   1.707 +        NS_ENSURE_SUCCESS(rv, rv);
   1.708 +      }
   1.709 +
   1.710 +      if (currentSchemaVersion < 19) {
   1.711 +        rv = MigrateV19Up();
   1.712 +        NS_ENSURE_SUCCESS(rv, rv);
   1.713 +      }
   1.714 +
   1.715 +      // Firefox 13 uses schema version 19.
   1.716 +
   1.717 +      if (currentSchemaVersion < 20) {
   1.718 +        rv = MigrateV20Up();
   1.719 +        NS_ENSURE_SUCCESS(rv, rv);
   1.720 +      }
   1.721 +
   1.722 +      if (currentSchemaVersion < 21) {
   1.723 +        rv = MigrateV21Up();
   1.724 +        NS_ENSURE_SUCCESS(rv, rv);
   1.725 +      }
   1.726 +
   1.727 +      // Firefox 14 uses schema version 21.
   1.728 +
   1.729 +      if (currentSchemaVersion < 22) {
   1.730 +        rv = MigrateV22Up();
   1.731 +        NS_ENSURE_SUCCESS(rv, rv);
   1.732 +      }
   1.733 +
   1.734 +      // Firefox 22 uses schema version 22.
   1.735 +
   1.736 +      if (currentSchemaVersion < 23) {
   1.737 +        rv = MigrateV23Up();
   1.738 +        NS_ENSURE_SUCCESS(rv, rv);
   1.739 +      }
   1.740 +
   1.741 +      // Firefox 24 uses schema version 23.
   1.742 +
   1.743 +      // Schema Upgrades must add migration code here.
   1.744 +
   1.745 +      rv = UpdateBookmarkRootTitles();
   1.746 +      // We don't want a broken localization to cause us to think
   1.747 +      // the database is corrupt and needs to be replaced.
   1.748 +      MOZ_ASSERT(NS_SUCCEEDED(rv));
   1.749 +    }
   1.750 +  }
   1.751 +  else {
   1.752 +    // This is a new database, so we have to create all the tables and indices.
   1.753 +
   1.754 +    // moz_places.
   1.755 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES);
   1.756 +    NS_ENSURE_SUCCESS(rv, rv);
   1.757 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL);
   1.758 +    NS_ENSURE_SUCCESS(rv, rv);
   1.759 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FAVICON);
   1.760 +    NS_ENSURE_SUCCESS(rv, rv);
   1.761 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_REVHOST);
   1.762 +    NS_ENSURE_SUCCESS(rv, rv);
   1.763 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_VISITCOUNT);
   1.764 +    NS_ENSURE_SUCCESS(rv, rv);
   1.765 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FRECENCY);
   1.766 +    NS_ENSURE_SUCCESS(rv, rv);
   1.767 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_LASTVISITDATE);
   1.768 +    NS_ENSURE_SUCCESS(rv, rv);
   1.769 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
   1.770 +    NS_ENSURE_SUCCESS(rv, rv);
   1.771 +
   1.772 +    // moz_historyvisits.
   1.773 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS);
   1.774 +    NS_ENSURE_SUCCESS(rv, rv);
   1.775 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_PLACEDATE);
   1.776 +    NS_ENSURE_SUCCESS(rv, rv);
   1.777 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_FROMVISIT);
   1.778 +    NS_ENSURE_SUCCESS(rv, rv);
   1.779 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_VISITDATE);
   1.780 +    NS_ENSURE_SUCCESS(rv, rv);
   1.781 +
   1.782 +    // moz_inputhistory.
   1.783 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_INPUTHISTORY);
   1.784 +    NS_ENSURE_SUCCESS(rv, rv);
   1.785 +
   1.786 +    // moz_hosts.
   1.787 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS);
   1.788 +    NS_ENSURE_SUCCESS(rv, rv);
   1.789 +
   1.790 +    // moz_bookmarks.
   1.791 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS);
   1.792 +    NS_ENSURE_SUCCESS(rv, rv);
   1.793 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACETYPE);
   1.794 +    NS_ENSURE_SUCCESS(rv, rv);
   1.795 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PARENTPOSITION);
   1.796 +    NS_ENSURE_SUCCESS(rv, rv);
   1.797 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
   1.798 +    NS_ENSURE_SUCCESS(rv, rv);
   1.799 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
   1.800 +    NS_ENSURE_SUCCESS(rv, rv);
   1.801 +
   1.802 +    // moz_bookmarks_roots.
   1.803 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_ROOTS);
   1.804 +    NS_ENSURE_SUCCESS(rv, rv);
   1.805 +
   1.806 +    // moz_keywords.
   1.807 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_KEYWORDS);
   1.808 +    NS_ENSURE_SUCCESS(rv, rv);
   1.809 +
   1.810 +    // moz_favicons.
   1.811 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_FAVICONS);
   1.812 +    NS_ENSURE_SUCCESS(rv, rv);
   1.813 +
   1.814 +    // moz_anno_attributes.
   1.815 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ANNO_ATTRIBUTES);
   1.816 +    NS_ENSURE_SUCCESS(rv, rv);
   1.817 +
   1.818 +    // moz_annos.
   1.819 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ANNOS);
   1.820 +    NS_ENSURE_SUCCESS(rv, rv);
   1.821 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ANNOS_PLACEATTRIBUTE);
   1.822 +    NS_ENSURE_SUCCESS(rv, rv);
   1.823 +
   1.824 +    // moz_items_annos.
   1.825 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ITEMS_ANNOS);
   1.826 +    NS_ENSURE_SUCCESS(rv, rv);
   1.827 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
   1.828 +    NS_ENSURE_SUCCESS(rv, rv);
   1.829 +
   1.830 +    // Initialize the bookmark roots in the new DB.
   1.831 +    rv = CreateBookmarkRoots();
   1.832 +    NS_ENSURE_SUCCESS(rv, rv);
   1.833 +  }
   1.834 +
   1.835 +  // Set the schema version to the current one.
   1.836 +  rv = mMainConn->SetSchemaVersion(DATABASE_SCHEMA_VERSION);
   1.837 +  NS_ENSURE_SUCCESS(rv, rv);
   1.838 +
   1.839 +  rv = transaction.Commit();
   1.840 +  NS_ENSURE_SUCCESS(rv, rv);
   1.841 +
   1.842 +  ForceWALCheckpoint();
   1.843 +
   1.844 +  // ANY FAILURE IN THIS METHOD WILL CAUSE US TO MARK THE DATABASE AS CORRUPT
   1.845 +  // AND TRY TO REPLACE IT.
   1.846 +  // DO NOT PUT HERE ANYTHING THAT IS NOT RELATED TO INITIALIZATION OR MODIFYING
   1.847 +  // THE DISK DATABASE.
   1.848 +
   1.849 +  return NS_OK;
   1.850 +}
   1.851 +
   1.852 +nsresult
   1.853 +Database::CreateBookmarkRoots()
   1.854 +{
   1.855 +  MOZ_ASSERT(NS_IsMainThread());
   1.856 +
   1.857 +  nsCOMPtr<nsIStringBundleService> bundleService =
   1.858 +    services::GetStringBundleService();
   1.859 +  NS_ENSURE_STATE(bundleService);
   1.860 +  nsCOMPtr<nsIStringBundle> bundle;
   1.861 +  nsresult rv = bundleService->CreateBundle(PLACES_BUNDLE, getter_AddRefs(bundle));
   1.862 +  if (NS_FAILED(rv)) return rv;
   1.863 +
   1.864 +  nsXPIDLString rootTitle;
   1.865 +  // The first root's title is an empty string.
   1.866 +  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("places"), rootTitle);
   1.867 +  if (NS_FAILED(rv)) return rv;
   1.868 +
   1.869 +  // Fetch the internationalized folder name from the string bundle.
   1.870 +  rv = bundle->GetStringFromName(MOZ_UTF16("BookmarksMenuFolderTitle"),
   1.871 +                                 getter_Copies(rootTitle));
   1.872 +  if (NS_FAILED(rv)) return rv;
   1.873 +  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("menu"), rootTitle);
   1.874 +  if (NS_FAILED(rv)) return rv;
   1.875 +
   1.876 +  rv = bundle->GetStringFromName(MOZ_UTF16("BookmarksToolbarFolderTitle"),
   1.877 +                                 getter_Copies(rootTitle));
   1.878 +  if (NS_FAILED(rv)) return rv;
   1.879 +  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("toolbar"), rootTitle);
   1.880 +  if (NS_FAILED(rv)) return rv;
   1.881 +
   1.882 +  rv = bundle->GetStringFromName(MOZ_UTF16("TagsFolderTitle"),
   1.883 +                                 getter_Copies(rootTitle));
   1.884 +  if (NS_FAILED(rv)) return rv;
   1.885 +  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("tags"), rootTitle);
   1.886 +  if (NS_FAILED(rv)) return rv;
   1.887 +
   1.888 +  rv = bundle->GetStringFromName(MOZ_UTF16("UnsortedBookmarksFolderTitle"),
   1.889 +                                 getter_Copies(rootTitle));
   1.890 +  if (NS_FAILED(rv)) return rv;
   1.891 +  rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("unfiled"), rootTitle);
   1.892 +  if (NS_FAILED(rv)) return rv;
   1.893 +
   1.894 +#if DEBUG
   1.895 +  nsCOMPtr<mozIStorageStatement> stmt;
   1.896 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
   1.897 +    "SELECT "
   1.898 +      "(SELECT COUNT(*) FROM moz_bookmarks), "
   1.899 +      "(SELECT COUNT(*) FROM moz_bookmarks_roots), "
   1.900 +      "(SELECT SUM(position) FROM moz_bookmarks WHERE "
   1.901 +        "id IN (SELECT folder_id FROM moz_bookmarks_roots))"
   1.902 +  ), getter_AddRefs(stmt));
   1.903 +  if (NS_FAILED(rv)) return rv;
   1.904 +
   1.905 +  bool hasResult;
   1.906 +  rv = stmt->ExecuteStep(&hasResult);
   1.907 +  if (NS_FAILED(rv)) return rv;
   1.908 +  MOZ_ASSERT(hasResult);
   1.909 +  int32_t bookmarkCount = 0;
   1.910 +  rv = stmt->GetInt32(0, &bookmarkCount);
   1.911 +  if (NS_FAILED(rv)) return rv;
   1.912 +  int32_t rootCount = 0;
   1.913 +  rv = stmt->GetInt32(1, &rootCount);
   1.914 +  if (NS_FAILED(rv)) return rv;
   1.915 +  int32_t positionSum = 0;
   1.916 +  rv = stmt->GetInt32(2, &positionSum);
   1.917 +  if (NS_FAILED(rv)) return rv;
   1.918 +  MOZ_ASSERT(bookmarkCount == 5 && rootCount == 5 && positionSum == 6);
   1.919 +#endif
   1.920 +
   1.921 +  return NS_OK;
   1.922 +}
   1.923 +
   1.924 +nsresult
   1.925 +Database::InitFunctions()
   1.926 +{
   1.927 +  MOZ_ASSERT(NS_IsMainThread());
   1.928 +
   1.929 +  nsresult rv = GetUnreversedHostFunction::create(mMainConn);
   1.930 +  NS_ENSURE_SUCCESS(rv, rv);
   1.931 +  rv = MatchAutoCompleteFunction::create(mMainConn);
   1.932 +  NS_ENSURE_SUCCESS(rv, rv);
   1.933 +  rv = CalculateFrecencyFunction::create(mMainConn);
   1.934 +  NS_ENSURE_SUCCESS(rv, rv);
   1.935 +  rv = GenerateGUIDFunction::create(mMainConn);
   1.936 +  NS_ENSURE_SUCCESS(rv, rv);
   1.937 +  rv = FixupURLFunction::create(mMainConn);
   1.938 +  NS_ENSURE_SUCCESS(rv, rv);
   1.939 +  rv = FrecencyNotificationFunction::create(mMainConn);
   1.940 +  NS_ENSURE_SUCCESS(rv, rv);
   1.941 +
   1.942 +  return NS_OK;
   1.943 +}
   1.944 +
   1.945 +nsresult
   1.946 +Database::InitTempTriggers()
   1.947 +{
   1.948 +  MOZ_ASSERT(NS_IsMainThread());
   1.949 +
   1.950 +  nsresult rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERINSERT_TRIGGER);
   1.951 +  NS_ENSURE_SUCCESS(rv, rv);
   1.952 +  rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERDELETE_TRIGGER);
   1.953 +  NS_ENSURE_SUCCESS(rv, rv);
   1.954 +
   1.955 +  // Add the triggers that update the moz_hosts table as necessary.
   1.956 +  rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERINSERT_TRIGGER);
   1.957 +  NS_ENSURE_SUCCESS(rv, rv);
   1.958 +  rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERDELETE_TRIGGER);
   1.959 +  NS_ENSURE_SUCCESS(rv, rv);
   1.960 +  rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_FRECENCY_TRIGGER);
   1.961 +  NS_ENSURE_SUCCESS(rv, rv);
   1.962 +  rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_TYPED_TRIGGER);
   1.963 +  NS_ENSURE_SUCCESS(rv, rv);
   1.964 +
   1.965 +  return NS_OK;
   1.966 +}
   1.967 +
   1.968 +nsresult
   1.969 +Database::UpdateBookmarkRootTitles()
   1.970 +{
   1.971 +  MOZ_ASSERT(NS_IsMainThread());
   1.972 +
   1.973 +  nsCOMPtr<nsIStringBundleService> bundleService =
   1.974 +    services::GetStringBundleService();
   1.975 +  NS_ENSURE_STATE(bundleService);
   1.976 +
   1.977 +  nsCOMPtr<nsIStringBundle> bundle;
   1.978 +  nsresult rv = bundleService->CreateBundle(PLACES_BUNDLE, getter_AddRefs(bundle));
   1.979 +  if (NS_FAILED(rv)) return rv;
   1.980 +
   1.981 +  nsCOMPtr<mozIStorageAsyncStatement> stmt;
   1.982 +  rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
   1.983 +    "UPDATE moz_bookmarks SET title = :new_title WHERE id = "
   1.984 +      "(SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = :root_name)"
   1.985 +  ), getter_AddRefs(stmt));
   1.986 +  if (NS_FAILED(rv)) return rv;
   1.987 +
   1.988 +  nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
   1.989 +  rv = stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
   1.990 +  if (NS_FAILED(rv)) return rv;
   1.991 +
   1.992 +  const char *rootNames[] = { "menu", "toolbar", "tags", "unfiled" };
   1.993 +  const char *titleStringIDs[] = {
   1.994 +    "BookmarksMenuFolderTitle", "BookmarksToolbarFolderTitle",
   1.995 +    "TagsFolderTitle", "UnsortedBookmarksFolderTitle"
   1.996 +  };
   1.997 +
   1.998 +  for (uint32_t i = 0; i < ArrayLength(rootNames); ++i) {
   1.999 +    nsXPIDLString title;
  1.1000 +    rv = bundle->GetStringFromName(NS_ConvertASCIItoUTF16(titleStringIDs[i]).get(),
  1.1001 +                                   getter_Copies(title));
  1.1002 +    if (NS_FAILED(rv)) return rv;
  1.1003 +
  1.1004 +    nsCOMPtr<mozIStorageBindingParams> params;
  1.1005 +    rv = paramsArray->NewBindingParams(getter_AddRefs(params));
  1.1006 +    if (NS_FAILED(rv)) return rv;
  1.1007 +    rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"),
  1.1008 +                                      nsDependentCString(rootNames[i]));
  1.1009 +    if (NS_FAILED(rv)) return rv;
  1.1010 +    rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("new_title"),
  1.1011 +                                      NS_ConvertUTF16toUTF8(title));
  1.1012 +    if (NS_FAILED(rv)) return rv;
  1.1013 +    rv = paramsArray->AddParams(params);
  1.1014 +    if (NS_FAILED(rv)) return rv;
  1.1015 +  }
  1.1016 +
  1.1017 +  rv = stmt->BindParameters(paramsArray);
  1.1018 +  if (NS_FAILED(rv)) return rv;
  1.1019 +  nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
  1.1020 +  rv = stmt->ExecuteAsync(nullptr, getter_AddRefs(pendingStmt));
  1.1021 +  if (NS_FAILED(rv)) return rv;
  1.1022 +
  1.1023 +  return NS_OK;
  1.1024 +}
  1.1025 +
  1.1026 +nsresult
  1.1027 +Database::CheckAndUpdateGUIDs()
  1.1028 +{
  1.1029 +  MOZ_ASSERT(NS_IsMainThread());
  1.1030 +
  1.1031 +  // First, import any bookmark guids already set by Sync.
  1.1032 +  nsCOMPtr<mozIStorageStatement> updateStmt;
  1.1033 +  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1034 +    "UPDATE moz_bookmarks "
  1.1035 +    "SET guid = :guid "
  1.1036 +    "WHERE id = :item_id "
  1.1037 +  ), getter_AddRefs(updateStmt));
  1.1038 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1039 +
  1.1040 +  nsCOMPtr<mozIStorageStatement> stmt;
  1.1041 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1042 +    "SELECT item_id, content "
  1.1043 +    "FROM moz_items_annos "
  1.1044 +    "JOIN moz_anno_attributes "
  1.1045 +    "WHERE name = :anno_name "
  1.1046 +  ), getter_AddRefs(stmt));
  1.1047 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1048 +  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
  1.1049 +                                  SYNCGUID_ANNO);
  1.1050 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1051 +
  1.1052 +  bool hasResult;
  1.1053 +  while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
  1.1054 +    int64_t itemId;
  1.1055 +    rv = stmt->GetInt64(0, &itemId);
  1.1056 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1057 +    nsAutoCString guid;
  1.1058 +    rv = stmt->GetUTF8String(1, guid);
  1.1059 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1060 +
  1.1061 +    // If we have an invalid guid, we don't need to do any more work.
  1.1062 +    if (!IsValidGUID(guid)) {
  1.1063 +      continue;
  1.1064 +    }
  1.1065 +
  1.1066 +    mozStorageStatementScoper updateScoper(updateStmt);
  1.1067 +    rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), itemId);
  1.1068 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1069 +    rv = updateStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
  1.1070 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1071 +    rv = updateStmt->Execute();
  1.1072 +    if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
  1.1073 +      // We just tried to insert a duplicate guid.  Ignore this error, and we
  1.1074 +      // will generate a new one next.
  1.1075 +      continue;
  1.1076 +    }
  1.1077 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1078 +  }
  1.1079 +
  1.1080 +  // Now, remove all the bookmark guid annotations that we just imported.
  1.1081 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1082 +    "DELETE FROM moz_items_annos "
  1.1083 +    "WHERE anno_attribute_id = ( "
  1.1084 +      "SELECT id "
  1.1085 +      "FROM moz_anno_attributes "
  1.1086 +      "WHERE name = :anno_name "
  1.1087 +    ") "
  1.1088 +  ), getter_AddRefs(stmt));
  1.1089 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1090 +  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
  1.1091 +                                  SYNCGUID_ANNO);
  1.1092 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1093 +
  1.1094 +  rv = stmt->Execute();
  1.1095 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1096 +
  1.1097 +  // Next, generate guids for any bookmark that does not already have one.
  1.1098 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1099 +    "UPDATE moz_bookmarks "
  1.1100 +    "SET guid = GENERATE_GUID() "
  1.1101 +    "WHERE guid IS NULL "
  1.1102 +  ), getter_AddRefs(stmt));
  1.1103 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1104 +
  1.1105 +  rv = stmt->Execute();
  1.1106 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1107 +
  1.1108 +  // Now, import any history guids already set by Sync.
  1.1109 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1110 +    "UPDATE moz_places "
  1.1111 +    "SET guid = :guid "
  1.1112 +    "WHERE id = :place_id "
  1.1113 +  ), getter_AddRefs(updateStmt));
  1.1114 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1115 +
  1.1116 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1117 +    "SELECT place_id, content "
  1.1118 +    "FROM moz_annos "
  1.1119 +    "JOIN moz_anno_attributes "
  1.1120 +    "WHERE name = :anno_name "
  1.1121 +  ), getter_AddRefs(stmt));
  1.1122 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1123 +  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
  1.1124 +                                  SYNCGUID_ANNO);
  1.1125 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1126 +
  1.1127 +  while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
  1.1128 +    int64_t placeId;
  1.1129 +    rv = stmt->GetInt64(0, &placeId);
  1.1130 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1131 +    nsAutoCString guid;
  1.1132 +    rv = stmt->GetUTF8String(1, guid);
  1.1133 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1134 +
  1.1135 +    // If we have an invalid guid, we don't need to do any more work.
  1.1136 +    if (!IsValidGUID(guid)) {
  1.1137 +      continue;
  1.1138 +    }
  1.1139 +
  1.1140 +    mozStorageStatementScoper updateScoper(updateStmt);
  1.1141 +    rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("place_id"), placeId);
  1.1142 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1143 +    rv = updateStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
  1.1144 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1145 +    rv = updateStmt->Execute();
  1.1146 +    if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
  1.1147 +      // We just tried to insert a duplicate guid.  Ignore this error, and we
  1.1148 +      // will generate a new one next.
  1.1149 +      continue;
  1.1150 +    }
  1.1151 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1152 +  }
  1.1153 +
  1.1154 +  // Now, remove all the place guid annotations that we just imported.
  1.1155 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1156 +    "DELETE FROM moz_annos "
  1.1157 +    "WHERE anno_attribute_id = ( "
  1.1158 +      "SELECT id "
  1.1159 +      "FROM moz_anno_attributes "
  1.1160 +      "WHERE name = :anno_name "
  1.1161 +    ") "
  1.1162 +  ), getter_AddRefs(stmt));
  1.1163 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1164 +  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
  1.1165 +                                  SYNCGUID_ANNO);
  1.1166 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1167 +
  1.1168 +  rv = stmt->Execute();
  1.1169 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1170 +
  1.1171 +  // Finally, generate guids for any places that do not already have one.
  1.1172 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1173 +    "UPDATE moz_places "
  1.1174 +    "SET guid = GENERATE_GUID() "
  1.1175 +    "WHERE guid IS NULL "
  1.1176 +  ), getter_AddRefs(stmt));
  1.1177 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1178 +
  1.1179 +  rv = stmt->Execute();
  1.1180 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1181 +
  1.1182 +  return NS_OK;
  1.1183 +}
  1.1184 +
  1.1185 +nsresult
  1.1186 +Database::MigrateV7Up() 
  1.1187 +{
  1.1188 +  MOZ_ASSERT(NS_IsMainThread());
  1.1189 +
  1.1190 +  // Some old v6 databases come from alpha versions that missed indices.
  1.1191 +  // Just bail out and replace the database in such a case.
  1.1192 +  bool URLUniqueIndexExists = false;
  1.1193 +  nsresult rv = mMainConn->IndexExists(NS_LITERAL_CSTRING(
  1.1194 +    "moz_places_url_uniqueindex"
  1.1195 +  ), &URLUniqueIndexExists);
  1.1196 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1197 +  if (!URLUniqueIndexExists) {
  1.1198 +    return NS_ERROR_FILE_CORRUPTED;
  1.1199 +  }
  1.1200 +
  1.1201 +  mozStorageTransaction transaction(mMainConn, false);
  1.1202 +
  1.1203 +  // We need an index on lastModified to catch quickly last modified bookmark
  1.1204 +  // title for tag container's children. This will be useful for Sync, too.
  1.1205 +  bool lastModIndexExists = false;
  1.1206 +  rv = mMainConn->IndexExists(
  1.1207 +    NS_LITERAL_CSTRING("moz_bookmarks_itemlastmodifiedindex"),
  1.1208 +    &lastModIndexExists);
  1.1209 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1210 +
  1.1211 +  if (!lastModIndexExists) {
  1.1212 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
  1.1213 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1214 +  }
  1.1215 +
  1.1216 +  // We need to do a one-time change of the moz_historyvisits.pageindex
  1.1217 +  // to speed up finding last visit date when joinin with moz_places.
  1.1218 +  // See bug 392399 for more details.
  1.1219 +  bool pageIndexExists = false;
  1.1220 +  rv = mMainConn->IndexExists(
  1.1221 +    NS_LITERAL_CSTRING("moz_historyvisits_pageindex"), &pageIndexExists);
  1.1222 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1223 +
  1.1224 +  if (pageIndexExists) {
  1.1225 +    // drop old index
  1.1226 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1227 +        "DROP INDEX IF EXISTS moz_historyvisits_pageindex"));
  1.1228 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1229 +
  1.1230 +    // create the new multi-column index
  1.1231 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_PLACEDATE);
  1.1232 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1233 +  }
  1.1234 +
  1.1235 +  // for existing profiles, we may not have a frecency column
  1.1236 +  nsCOMPtr<mozIStorageStatement> hasFrecencyStatement;
  1.1237 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1238 +      "SELECT frecency FROM moz_places"),
  1.1239 +    getter_AddRefs(hasFrecencyStatement));
  1.1240 +
  1.1241 +  if (NS_FAILED(rv)) {
  1.1242 +    // Add frecency column to moz_places, default to -1 so that all the
  1.1243 +    // frecencies are invalid
  1.1244 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1245 +        "ALTER TABLE moz_places ADD frecency INTEGER DEFAULT -1 NOT NULL"));
  1.1246 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1247 +
  1.1248 +    // create index for the frecency column
  1.1249 +    // XXX multi column index with typed, and visit_count?
  1.1250 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FRECENCY);
  1.1251 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1252 +
  1.1253 +    // Invalidate all frecencies, since they need recalculation.
  1.1254 +    nsCOMPtr<mozIStorageAsyncStatement> stmt = GetAsyncStatement(
  1.1255 +      "UPDATE moz_places SET frecency = ( "
  1.1256 +        "CASE "
  1.1257 +        "WHEN url BETWEEN 'place:' AND 'place;' "
  1.1258 +        "THEN 0 "
  1.1259 +        "ELSE -1 "
  1.1260 +        "END "
  1.1261 +      ") "
  1.1262 +    );
  1.1263 +    NS_ENSURE_STATE(stmt);
  1.1264 +    nsCOMPtr<mozIStoragePendingStatement> ps;
  1.1265 +    (void)stmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
  1.1266 +  }
  1.1267 +
  1.1268 +  // Temporary migration code for bug 396300
  1.1269 +  nsCOMPtr<mozIStorageStatement> moveUnfiledBookmarks;
  1.1270 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1271 +      "UPDATE moz_bookmarks "
  1.1272 +      "SET parent = ("
  1.1273 +        "SELECT folder_id "
  1.1274 +        "FROM moz_bookmarks_roots "
  1.1275 +        "WHERE root_name = :root_name "
  1.1276 +      ") "
  1.1277 +      "WHERE type = :item_type "
  1.1278 +      "AND parent = ("
  1.1279 +        "SELECT folder_id "
  1.1280 +        "FROM moz_bookmarks_roots "
  1.1281 +        "WHERE root_name = :parent_name "
  1.1282 +      ")"),
  1.1283 +    getter_AddRefs(moveUnfiledBookmarks));
  1.1284 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1285 +  rv = moveUnfiledBookmarks->BindUTF8StringByName(
  1.1286 +    NS_LITERAL_CSTRING("root_name"), NS_LITERAL_CSTRING("unfiled")
  1.1287 +  );
  1.1288 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1289 +  rv = moveUnfiledBookmarks->BindInt32ByName(
  1.1290 +    NS_LITERAL_CSTRING("item_type"), nsINavBookmarksService::TYPE_BOOKMARK
  1.1291 +  );
  1.1292 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1293 +  rv = moveUnfiledBookmarks->BindUTF8StringByName(
  1.1294 +    NS_LITERAL_CSTRING("parent_name"), NS_LITERAL_CSTRING("places")
  1.1295 +  );
  1.1296 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1297 +  rv = moveUnfiledBookmarks->Execute();
  1.1298 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1299 +
  1.1300 +  // Create a statement to test for trigger creation
  1.1301 +  nsCOMPtr<mozIStorageStatement> triggerDetection;
  1.1302 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1303 +      "SELECT name "
  1.1304 +      "FROM sqlite_master "
  1.1305 +      "WHERE type = 'trigger' "
  1.1306 +      "AND name = :trigger_name"),
  1.1307 +    getter_AddRefs(triggerDetection));
  1.1308 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1309 +
  1.1310 +  // Check for existence
  1.1311 +  bool triggerExists;
  1.1312 +  rv = triggerDetection->BindUTF8StringByName(
  1.1313 +    NS_LITERAL_CSTRING("trigger_name"),
  1.1314 +    NS_LITERAL_CSTRING("moz_historyvisits_afterinsert_v1_trigger")
  1.1315 +  );
  1.1316 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1317 +  rv = triggerDetection->ExecuteStep(&triggerExists);
  1.1318 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1319 +  rv = triggerDetection->Reset();
  1.1320 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1321 +
  1.1322 +  // We need to create two triggers on moz_historyvists to maintain the
  1.1323 +  // accuracy of moz_places.visit_count.  For this to work, we must ensure that
  1.1324 +  // all moz_places.visit_count values are correct.
  1.1325 +  // See bug 416313 for details.
  1.1326 +  if (!triggerExists) {
  1.1327 +    // First, we do a one-time reset of all the moz_places.visit_count values.
  1.1328 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1329 +        "UPDATE moz_places SET visit_count = "
  1.1330 +          "(SELECT count(*) FROM moz_historyvisits "
  1.1331 +           "WHERE place_id = moz_places.id "
  1.1332 +            "AND visit_type NOT IN ") +
  1.1333 +              nsPrintfCString("(0,%d,%d,%d) ",
  1.1334 +                              nsINavHistoryService::TRANSITION_EMBED,
  1.1335 +                              nsINavHistoryService::TRANSITION_FRAMED_LINK,
  1.1336 +                              nsINavHistoryService::TRANSITION_DOWNLOAD) +
  1.1337 +          NS_LITERAL_CSTRING(")"));
  1.1338 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1339 +
  1.1340 +    // We used to create two triggers here, but we no longer need that with
  1.1341 +    // schema version eight and greater.  We've removed their creation here as
  1.1342 +    // a result.
  1.1343 +  }
  1.1344 +
  1.1345 +  // Check for existence
  1.1346 +  rv = triggerDetection->BindUTF8StringByName(
  1.1347 +    NS_LITERAL_CSTRING("trigger_name"),
  1.1348 +    NS_LITERAL_CSTRING("moz_bookmarks_beforedelete_v1_trigger")
  1.1349 +  );
  1.1350 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1351 +  rv = triggerDetection->ExecuteStep(&triggerExists);
  1.1352 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1353 +  rv = triggerDetection->Reset();
  1.1354 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1355 +
  1.1356 +  // We need to create one trigger on moz_bookmarks to remove unused keywords.
  1.1357 +  // See bug 421180 for details.
  1.1358 +  if (!triggerExists) {
  1.1359 +    // First, remove any existing dangling keywords
  1.1360 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1361 +        "DELETE FROM moz_keywords "
  1.1362 +        "WHERE id IN ("
  1.1363 +          "SELECT k.id "
  1.1364 +          "FROM moz_keywords k "
  1.1365 +          "LEFT OUTER JOIN moz_bookmarks b "
  1.1366 +          "ON b.keyword_id = k.id "
  1.1367 +          "WHERE b.id IS NULL"
  1.1368 +        ")"));
  1.1369 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1370 +  }
  1.1371 +
  1.1372 +  // Add the moz_inputhistory table, if missing.
  1.1373 +  bool tableExists = false;
  1.1374 +  rv = mMainConn->TableExists(NS_LITERAL_CSTRING("moz_inputhistory"),
  1.1375 +                              &tableExists);
  1.1376 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1377 +  if (!tableExists) {
  1.1378 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_INPUTHISTORY);
  1.1379 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1380 +  }
  1.1381 +
  1.1382 +  return transaction.Commit();
  1.1383 +}
  1.1384 +
  1.1385 +
  1.1386 +nsresult
  1.1387 +Database::MigrateV8Up()
  1.1388 +{
  1.1389 +  MOZ_ASSERT(NS_IsMainThread());
  1.1390 +  mozStorageTransaction transaction(mMainConn, false);
  1.1391 +
  1.1392 +  nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1393 +      "DROP TRIGGER IF EXISTS moz_historyvisits_afterinsert_v1_trigger"));
  1.1394 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1395 +
  1.1396 +  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1397 +      "DROP TRIGGER IF EXISTS moz_historyvisits_afterdelete_v1_trigger"));
  1.1398 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1399 +
  1.1400 +
  1.1401 +  // bug #381795 - remove unused indexes
  1.1402 +  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1403 +      "DROP INDEX IF EXISTS moz_places_titleindex"));
  1.1404 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1405 +  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1406 +      "DROP INDEX IF EXISTS moz_annos_item_idindex"));
  1.1407 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1408 +  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1409 +      "DROP INDEX IF EXISTS moz_annos_place_idindex"));
  1.1410 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1411 +
  1.1412 +  // Do a one-time re-creation of the moz_annos indexes (bug 415201)
  1.1413 +  bool oldIndexExists = false;
  1.1414 +  rv = mMainConn->IndexExists(NS_LITERAL_CSTRING("moz_annos_attributesindex"), &oldIndexExists);
  1.1415 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1416 +  if (oldIndexExists) {
  1.1417 +    // drop old uri annos index
  1.1418 +    rv = mMainConn->ExecuteSimpleSQL(
  1.1419 +        NS_LITERAL_CSTRING("DROP INDEX moz_annos_attributesindex"));
  1.1420 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1421 +
  1.1422 +    // create new uri annos index
  1.1423 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ANNOS_PLACEATTRIBUTE);
  1.1424 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1425 +
  1.1426 +    // drop old item annos index
  1.1427 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1428 +        "DROP INDEX IF EXISTS moz_items_annos_attributesindex"));
  1.1429 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1430 +
  1.1431 +    // create new item annos index
  1.1432 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
  1.1433 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1434 +  }
  1.1435 +
  1.1436 +  return transaction.Commit();
  1.1437 +}
  1.1438 +
  1.1439 +
  1.1440 +nsresult
  1.1441 +Database::MigrateV9Up()
  1.1442 +{
  1.1443 +  MOZ_ASSERT(NS_IsMainThread());
  1.1444 +  mozStorageTransaction transaction(mMainConn, false);
  1.1445 +  // Added in Bug 488966.  The last_visit_date column caches the last
  1.1446 +  // visit date, this enhances SELECT performances when we
  1.1447 +  // need to sort visits by visit date.
  1.1448 +  // The cached value is synced by triggers on every added or removed visit.
  1.1449 +  // See nsPlacesTriggers.h for details on the triggers.
  1.1450 +  bool oldIndexExists = false;
  1.1451 +  nsresult rv = mMainConn->IndexExists(
  1.1452 +    NS_LITERAL_CSTRING("moz_places_lastvisitdateindex"), &oldIndexExists);
  1.1453 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1454 +
  1.1455 +  if (!oldIndexExists) {
  1.1456 +    // Add last_visit_date column to moz_places.
  1.1457 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1458 +        "ALTER TABLE moz_places ADD last_visit_date INTEGER"));
  1.1459 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1460 +
  1.1461 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_LASTVISITDATE);
  1.1462 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1463 +
  1.1464 +    // Now let's sync the column contents with real visit dates.
  1.1465 +    // This query can be really slow due to disk access, since it will basically
  1.1466 +    // dupe the table contents in the journal file, and then write them down
  1.1467 +    // in the database.
  1.1468 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1469 +        "UPDATE moz_places SET last_visit_date = "
  1.1470 +          "(SELECT MAX(visit_date) "
  1.1471 +           "FROM moz_historyvisits "
  1.1472 +           "WHERE place_id = moz_places.id)"));
  1.1473 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1474 +  }
  1.1475 +
  1.1476 +  return transaction.Commit();
  1.1477 +}
  1.1478 +
  1.1479 +
  1.1480 +nsresult
  1.1481 +Database::MigrateV10Up()
  1.1482 +{
  1.1483 +  MOZ_ASSERT(NS_IsMainThread());
  1.1484 +  // LastModified is set to the same value as dateAdded on item creation.
  1.1485 +  // This way we can use lastModified index to sort.
  1.1486 +  nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1487 +      "UPDATE moz_bookmarks SET lastModified = dateAdded "
  1.1488 +      "WHERE lastModified IS NULL"));
  1.1489 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1490 +
  1.1491 +  return NS_OK;
  1.1492 +}
  1.1493 +
  1.1494 +
  1.1495 +nsresult
  1.1496 +Database::MigrateV11Up()
  1.1497 +{
  1.1498 +  MOZ_ASSERT(NS_IsMainThread());
  1.1499 +  // Temp tables are going away.
  1.1500 +  // For triggers correctness, every time we pass through this migration
  1.1501 +  // step, we must ensure correctness of visit_count values.
  1.1502 +  nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1503 +    "UPDATE moz_places SET visit_count = "
  1.1504 +      "(SELECT count(*) FROM moz_historyvisits "
  1.1505 +       "WHERE place_id = moz_places.id "
  1.1506 +        "AND visit_type NOT IN ") +
  1.1507 +          nsPrintfCString("(0,%d,%d,%d) ",
  1.1508 +                          nsINavHistoryService::TRANSITION_EMBED,
  1.1509 +                          nsINavHistoryService::TRANSITION_FRAMED_LINK,
  1.1510 +                          nsINavHistoryService::TRANSITION_DOWNLOAD) +
  1.1511 +      NS_LITERAL_CSTRING(")")
  1.1512 +  );
  1.1513 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1514 +
  1.1515 +  // For existing profiles, we may not have a moz_bookmarks.guid column
  1.1516 +  nsCOMPtr<mozIStorageStatement> hasGuidStatement;
  1.1517 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1518 +      "SELECT guid FROM moz_bookmarks"),
  1.1519 +    getter_AddRefs(hasGuidStatement));
  1.1520 +
  1.1521 +  if (NS_FAILED(rv)) {
  1.1522 +    // moz_bookmarks grew a guid column.  Add the column, but do not populate it
  1.1523 +    // with anything just yet.  We will do that soon.
  1.1524 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1525 +      "ALTER TABLE moz_bookmarks "
  1.1526 +      "ADD COLUMN guid TEXT"
  1.1527 +    ));
  1.1528 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1529 +
  1.1530 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
  1.1531 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1532 +
  1.1533 +    // moz_places grew a guid column. Add the column, but do not populate it
  1.1534 +    // with anything just yet. We will do that soon.
  1.1535 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1536 +      "ALTER TABLE moz_places "
  1.1537 +      "ADD COLUMN guid TEXT"
  1.1538 +    ));
  1.1539 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1540 +
  1.1541 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
  1.1542 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1543 +  }
  1.1544 +
  1.1545 +  // We need to update our guids before we do any real database work.
  1.1546 +  rv = CheckAndUpdateGUIDs();
  1.1547 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1548 +
  1.1549 +  return NS_OK;
  1.1550 +}
  1.1551 +
  1.1552 +nsresult
  1.1553 +Database::MigrateV13Up()
  1.1554 +{
  1.1555 +  MOZ_ASSERT(NS_IsMainThread());
  1.1556 +
  1.1557 +  // Dynamic containers are no longer supported.
  1.1558 +  nsCOMPtr<mozIStorageAsyncStatement> deleteDynContainersStmt;
  1.1559 +  nsresult rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  1.1560 +      "DELETE FROM moz_bookmarks WHERE type = :item_type"),
  1.1561 +    getter_AddRefs(deleteDynContainersStmt));
  1.1562 +  rv = deleteDynContainersStmt->BindInt32ByName(
  1.1563 +    NS_LITERAL_CSTRING("item_type"),
  1.1564 +    nsINavBookmarksService::TYPE_DYNAMIC_CONTAINER
  1.1565 +  );
  1.1566 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1567 +  nsCOMPtr<mozIStoragePendingStatement> ps;
  1.1568 +  rv = deleteDynContainersStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
  1.1569 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1570 +
  1.1571 +  return NS_OK;
  1.1572 +}
  1.1573 +
  1.1574 +nsresult
  1.1575 +Database::MigrateV14Up()
  1.1576 +{
  1.1577 +  MOZ_ASSERT(NS_IsMainThread());
  1.1578 +
  1.1579 +  // For existing profiles, we may not have a moz_favicons.guid column.
  1.1580 +  // Add it here. We want it to be unique, but ALTER TABLE doesn't allow
  1.1581 +  // a uniqueness constraint, so the index must be created separately.
  1.1582 +  nsCOMPtr<mozIStorageStatement> hasGuidStatement;
  1.1583 +  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1584 +      "SELECT guid FROM moz_favicons"),
  1.1585 +    getter_AddRefs(hasGuidStatement));
  1.1586 +
  1.1587 +  if (NS_FAILED(rv)) {
  1.1588 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1589 +      "ALTER TABLE moz_favicons "
  1.1590 +      "ADD COLUMN guid TEXT"
  1.1591 +    ));
  1.1592 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1593 +
  1.1594 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_FAVICONS_GUID);
  1.1595 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1596 +  }
  1.1597 +
  1.1598 +  // Generate GUID for any favicon missing it.
  1.1599 +  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1600 +    "UPDATE moz_favicons "
  1.1601 +    "SET guid = GENERATE_GUID() "
  1.1602 +    "WHERE guid ISNULL "
  1.1603 +  ));
  1.1604 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1605 +
  1.1606 +  return NS_OK;
  1.1607 +}
  1.1608 +
  1.1609 +nsresult
  1.1610 +Database::MigrateV15Up()
  1.1611 +{
  1.1612 +  MOZ_ASSERT(NS_IsMainThread());
  1.1613 +
  1.1614 +  // Drop moz_bookmarks_beforedelete_v1_trigger, since it's more expensive than
  1.1615 +  // useful.
  1.1616 +  nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1617 +    "DROP TRIGGER IF EXISTS moz_bookmarks_beforedelete_v1_trigger"
  1.1618 +  ));
  1.1619 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1620 +
  1.1621 +  // Remove any orphan keywords.
  1.1622 +  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1623 +    "DELETE FROM moz_keywords "
  1.1624 +    "WHERE NOT EXISTS ( "
  1.1625 +      "SELECT id "
  1.1626 +      "FROM moz_bookmarks "
  1.1627 +      "WHERE keyword_id = moz_keywords.id "
  1.1628 +    ")"
  1.1629 +  ));
  1.1630 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1631 +
  1.1632 +  return NS_OK;
  1.1633 +}
  1.1634 +
  1.1635 +nsresult
  1.1636 +Database::MigrateV16Up()
  1.1637 +{
  1.1638 +  MOZ_ASSERT(NS_IsMainThread());
  1.1639 +
  1.1640 +  // Due to Bug 715268 downgraded and then upgraded profiles may lack favicons
  1.1641 +  // guids, so fillup any missing ones.
  1.1642 +  nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1643 +    "UPDATE moz_favicons "
  1.1644 +    "SET guid = GENERATE_GUID() "
  1.1645 +    "WHERE guid ISNULL "
  1.1646 +  ));
  1.1647 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1648 +
  1.1649 +  return NS_OK;
  1.1650 +}
  1.1651 +
  1.1652 +nsresult
  1.1653 +Database::MigrateV17Up()
  1.1654 +{
  1.1655 +  MOZ_ASSERT(NS_IsMainThread());
  1.1656 +
  1.1657 +  bool tableExists = false;
  1.1658 +
  1.1659 +  nsresult rv = mMainConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
  1.1660 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1661 +
  1.1662 +  if (!tableExists) {
  1.1663 +    // For anyone who used in-development versions of this autocomplete,
  1.1664 +    // drop the old tables and its indexes.
  1.1665 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1666 +      "DROP INDEX IF EXISTS moz_hostnames_frecencyindex"
  1.1667 +    ));
  1.1668 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1669 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1670 +      "DROP TABLE IF EXISTS moz_hostnames"
  1.1671 +    ));
  1.1672 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1673 +
  1.1674 +    // Add the moz_hosts table so we can get hostnames for URL autocomplete.
  1.1675 +    rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS);
  1.1676 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1677 +  }
  1.1678 +
  1.1679 +  // Fill the moz_hosts table with all the domains in moz_places.
  1.1680 +  nsCOMPtr<mozIStorageAsyncStatement> fillHostsStmt;
  1.1681 +  rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  1.1682 +    "INSERT OR IGNORE INTO moz_hosts (host, frecency) "
  1.1683 +        "SELECT fixup_url(get_unreversed_host(h.rev_host)) AS host, "
  1.1684 +               "(SELECT MAX(frecency) FROM moz_places "
  1.1685 +                "WHERE rev_host = h.rev_host "
  1.1686 +                   "OR rev_host = h.rev_host || 'www.' "
  1.1687 +               ") AS frecency "
  1.1688 +        "FROM moz_places h "
  1.1689 +        "WHERE LENGTH(h.rev_host) > 1 "
  1.1690 +        "GROUP BY h.rev_host"
  1.1691 +  ), getter_AddRefs(fillHostsStmt));
  1.1692 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1693 +
  1.1694 +  nsCOMPtr<mozIStoragePendingStatement> ps;
  1.1695 +  rv = fillHostsStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
  1.1696 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1697 +
  1.1698 +  return NS_OK;
  1.1699 +}
  1.1700 +
  1.1701 +nsresult
  1.1702 +Database::MigrateV18Up()
  1.1703 +{
  1.1704 +  MOZ_ASSERT(NS_IsMainThread());
  1.1705 +
  1.1706 +  // moz_hosts should distinguish on typed entries.
  1.1707 +
  1.1708 +  // Check if the profile already has a typed column.
  1.1709 +  nsCOMPtr<mozIStorageStatement> stmt;
  1.1710 +  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1711 +    "SELECT typed FROM moz_hosts"
  1.1712 +  ), getter_AddRefs(stmt));
  1.1713 +  if (NS_FAILED(rv)) {
  1.1714 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1715 +      "ALTER TABLE moz_hosts ADD COLUMN typed NOT NULL DEFAULT 0"
  1.1716 +    ));
  1.1717 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1718 +  }
  1.1719 +
  1.1720 +  // With the addition of the typed column the covering index loses its
  1.1721 +  // advantages.  On the other side querying on host and (optionally) typed
  1.1722 +  // largely restricts the number of results, making scans decently fast.
  1.1723 +  rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1724 +    "DROP INDEX IF EXISTS moz_hosts_frecencyhostindex"
  1.1725 +  ));
  1.1726 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1727 +
  1.1728 +  // Update typed data.
  1.1729 +  nsCOMPtr<mozIStorageAsyncStatement> updateTypedStmt;
  1.1730 +  rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  1.1731 +    "UPDATE moz_hosts SET typed = 1 WHERE host IN ( "
  1.1732 +      "SELECT fixup_url(get_unreversed_host(rev_host)) "
  1.1733 +      "FROM moz_places WHERE typed = 1 "
  1.1734 +    ") "
  1.1735 +  ), getter_AddRefs(updateTypedStmt));
  1.1736 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1737 +
  1.1738 +  nsCOMPtr<mozIStoragePendingStatement> ps;
  1.1739 +  rv = updateTypedStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
  1.1740 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1741 +
  1.1742 +  return NS_OK;
  1.1743 +}
  1.1744 +
  1.1745 +nsresult
  1.1746 +Database::MigrateV19Up()
  1.1747 +{
  1.1748 +  MOZ_ASSERT(NS_IsMainThread());
  1.1749 +
  1.1750 +  // Livemarks children are no longer bookmarks.
  1.1751 +
  1.1752 +  // Remove all children of folders annotated as livemarks.
  1.1753 +  nsCOMPtr<mozIStorageStatement> deleteLivemarksChildrenStmt;
  1.1754 +  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1755 +    "DELETE FROM moz_bookmarks WHERE parent IN("
  1.1756 +      "SELECT b.id FROM moz_bookmarks b "
  1.1757 +      "JOIN moz_items_annos a ON a.item_id = b.id "
  1.1758 +      "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
  1.1759 +      "WHERE b.type = :item_type AND n.name = :anno_name "
  1.1760 +    ")"
  1.1761 +  ), getter_AddRefs(deleteLivemarksChildrenStmt));
  1.1762 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1763 +  rv = deleteLivemarksChildrenStmt->BindUTF8StringByName(
  1.1764 +    NS_LITERAL_CSTRING("anno_name"), NS_LITERAL_CSTRING(LMANNO_FEEDURI)
  1.1765 +  );
  1.1766 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1767 +  rv = deleteLivemarksChildrenStmt->BindInt32ByName(
  1.1768 +    NS_LITERAL_CSTRING("item_type"), nsINavBookmarksService::TYPE_FOLDER
  1.1769 +  );
  1.1770 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1771 +  rv = deleteLivemarksChildrenStmt->Execute();
  1.1772 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1773 +
  1.1774 +  // Clear obsolete livemark prefs.
  1.1775 +  (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_seconds");
  1.1776 +  (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_limit_count");
  1.1777 +  (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_delay_time");
  1.1778 +
  1.1779 +  // Remove the old status annotations.
  1.1780 +  nsCOMPtr<mozIStorageStatement> deleteLivemarksAnnosStmt;
  1.1781 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1782 +    "DELETE FROM moz_items_annos WHERE anno_attribute_id IN("
  1.1783 +      "SELECT id FROM moz_anno_attributes "
  1.1784 +      "WHERE name IN (:anno_loading, :anno_loadfailed, :anno_expiration) "
  1.1785 +    ")"
  1.1786 +  ), getter_AddRefs(deleteLivemarksAnnosStmt));
  1.1787 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1788 +  rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
  1.1789 +    NS_LITERAL_CSTRING("anno_loading"), NS_LITERAL_CSTRING("livemark/loading")
  1.1790 +  );
  1.1791 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1792 +  rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
  1.1793 +    NS_LITERAL_CSTRING("anno_loadfailed"), NS_LITERAL_CSTRING("livemark/loadfailed")
  1.1794 +  );
  1.1795 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1796 +  rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
  1.1797 +    NS_LITERAL_CSTRING("anno_expiration"), NS_LITERAL_CSTRING("livemark/expiration")
  1.1798 +  );
  1.1799 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1800 +  rv = deleteLivemarksAnnosStmt->Execute();
  1.1801 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1802 +
  1.1803 +  // Remove orphan annotation names.
  1.1804 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1805 +    "DELETE FROM moz_anno_attributes "
  1.1806 +      "WHERE name IN (:anno_loading, :anno_loadfailed, :anno_expiration) "
  1.1807 +  ), getter_AddRefs(deleteLivemarksAnnosStmt));
  1.1808 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1809 +  rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
  1.1810 +    NS_LITERAL_CSTRING("anno_loading"), NS_LITERAL_CSTRING("livemark/loading")
  1.1811 +  );
  1.1812 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1813 +  rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
  1.1814 +    NS_LITERAL_CSTRING("anno_loadfailed"), NS_LITERAL_CSTRING("livemark/loadfailed")
  1.1815 +  );
  1.1816 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1817 +  rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
  1.1818 +    NS_LITERAL_CSTRING("anno_expiration"), NS_LITERAL_CSTRING("livemark/expiration")
  1.1819 +  );
  1.1820 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1821 +  rv = deleteLivemarksAnnosStmt->Execute();
  1.1822 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1823 +
  1.1824 +  return NS_OK;
  1.1825 +}
  1.1826 +
  1.1827 +nsresult
  1.1828 +Database::MigrateV20Up()
  1.1829 +{
  1.1830 +  MOZ_ASSERT(NS_IsMainThread());
  1.1831 +
  1.1832 +  // Remove obsolete bookmark GUID annotations.
  1.1833 +  nsCOMPtr<mozIStorageStatement> deleteOldBookmarkGUIDAnnosStmt;
  1.1834 +  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1835 +    "DELETE FROM moz_items_annos WHERE anno_attribute_id = ("
  1.1836 +      "SELECT id FROM moz_anno_attributes "
  1.1837 +      "WHERE name = :anno_guid"
  1.1838 +    ")"
  1.1839 +  ), getter_AddRefs(deleteOldBookmarkGUIDAnnosStmt));
  1.1840 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1841 +  rv = deleteOldBookmarkGUIDAnnosStmt->BindUTF8StringByName(
  1.1842 +    NS_LITERAL_CSTRING("anno_guid"), NS_LITERAL_CSTRING("placesInternal/GUID")
  1.1843 +  );
  1.1844 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1845 +  rv = deleteOldBookmarkGUIDAnnosStmt->Execute();
  1.1846 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1847 +
  1.1848 +  // Remove the orphan annotation name.
  1.1849 +  rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1850 +    "DELETE FROM moz_anno_attributes "
  1.1851 +      "WHERE name = :anno_guid"
  1.1852 +  ), getter_AddRefs(deleteOldBookmarkGUIDAnnosStmt));
  1.1853 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1854 +  rv = deleteOldBookmarkGUIDAnnosStmt->BindUTF8StringByName(
  1.1855 +    NS_LITERAL_CSTRING("anno_guid"), NS_LITERAL_CSTRING("placesInternal/GUID")
  1.1856 +  );
  1.1857 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1858 +  rv = deleteOldBookmarkGUIDAnnosStmt->Execute();
  1.1859 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1860 +
  1.1861 +  return NS_OK;
  1.1862 +}
  1.1863 +
  1.1864 +nsresult
  1.1865 +Database::MigrateV21Up()
  1.1866 +{
  1.1867 +  MOZ_ASSERT(NS_IsMainThread());
  1.1868 +
  1.1869 +  // Add a prefix column to moz_hosts.
  1.1870 +  nsCOMPtr<mozIStorageStatement> stmt;
  1.1871 +  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.1872 +    "SELECT prefix FROM moz_hosts"
  1.1873 +  ), getter_AddRefs(stmt));
  1.1874 +  if (NS_FAILED(rv)) {
  1.1875 +    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1876 +      "ALTER TABLE moz_hosts ADD COLUMN prefix"
  1.1877 +    ));
  1.1878 +    NS_ENSURE_SUCCESS(rv, rv);
  1.1879 +  }
  1.1880 +
  1.1881 +  return NS_OK;
  1.1882 +}
  1.1883 +
  1.1884 +nsresult
  1.1885 +Database::MigrateV22Up()
  1.1886 +{
  1.1887 +  MOZ_ASSERT(NS_IsMainThread());
  1.1888 +
  1.1889 +  // Reset all session IDs to 0 since we don't support them anymore.
  1.1890 +  // We don't set them to NULL to avoid breaking downgrades.
  1.1891 +  nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
  1.1892 +    "UPDATE moz_historyvisits SET session = 0"
  1.1893 +  ));
  1.1894 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1895 +
  1.1896 +  return NS_OK;
  1.1897 +}
  1.1898 +
  1.1899 +
  1.1900 +nsresult
  1.1901 +Database::MigrateV23Up()
  1.1902 +{
  1.1903 +  MOZ_ASSERT(NS_IsMainThread());
  1.1904 +
  1.1905 +  // Recalculate hosts prefixes.
  1.1906 +  nsCOMPtr<mozIStorageAsyncStatement> updatePrefixesStmt;
  1.1907 +  nsresult rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
  1.1908 +    "UPDATE moz_hosts SET prefix = ( " HOSTS_PREFIX_PRIORITY_FRAGMENT ") "
  1.1909 +  ), getter_AddRefs(updatePrefixesStmt));
  1.1910 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1911 +
  1.1912 +  nsCOMPtr<mozIStoragePendingStatement> ps;
  1.1913 +  rv = updatePrefixesStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
  1.1914 +  NS_ENSURE_SUCCESS(rv, rv);
  1.1915 +
  1.1916 +  return NS_OK;
  1.1917 +}
  1.1918 +
  1.1919 +void
  1.1920 +Database::Shutdown()
  1.1921 +{
  1.1922 +  MOZ_ASSERT(NS_IsMainThread());
  1.1923 +  MOZ_ASSERT(!mShuttingDown);
  1.1924 +  MOZ_ASSERT(!mClosed);
  1.1925 +
  1.1926 +  mShuttingDown = true;
  1.1927 +
  1.1928 +  mMainThreadStatements.FinalizeStatements();
  1.1929 +  mMainThreadAsyncStatements.FinalizeStatements();
  1.1930 +
  1.1931 +  nsRefPtr< FinalizeStatementCacheProxy<mozIStorageStatement> > event =
  1.1932 +    new FinalizeStatementCacheProxy<mozIStorageStatement>(
  1.1933 +          mAsyncThreadStatements, NS_ISUPPORTS_CAST(nsIObserver*, this)
  1.1934 +        );
  1.1935 +  DispatchToAsyncThread(event);
  1.1936 +
  1.1937 +  mClosed = true;
  1.1938 +
  1.1939 +  nsRefPtr<ConnectionCloseCallback> closeListener =
  1.1940 +    new ConnectionCloseCallback();
  1.1941 +  (void)mMainConn->AsyncClose(closeListener);
  1.1942 +}
  1.1943 +
  1.1944 +////////////////////////////////////////////////////////////////////////////////
  1.1945 +//// nsIObserver
  1.1946 +
  1.1947 +NS_IMETHODIMP
  1.1948 +Database::Observe(nsISupports *aSubject,
  1.1949 +                  const char *aTopic,
  1.1950 +                  const char16_t *aData)
  1.1951 +{
  1.1952 +  MOZ_ASSERT(NS_IsMainThread());
  1.1953 + 
  1.1954 +  if (strcmp(aTopic, TOPIC_PROFILE_CHANGE_TEARDOWN) == 0) {
  1.1955 +    // Tests simulating shutdown may cause multiple notifications.
  1.1956 +    if (mShuttingDown) {
  1.1957 +      return NS_OK;
  1.1958 +    }
  1.1959 +
  1.1960 +    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  1.1961 +    NS_ENSURE_STATE(os);
  1.1962 +
  1.1963 +    // If shutdown happens in the same mainthread loop as init, observers could
  1.1964 +    // handle the places-init-complete notification after xpcom-shutdown, when
  1.1965 +    // the connection does not exist anymore.  Removing those observers would
  1.1966 +    // be less expensive but may cause their RemoveObserver calls to throw.
  1.1967 +    // Thus notify the topic now, so they stop listening for it.
  1.1968 +    nsCOMPtr<nsISimpleEnumerator> e;
  1.1969 +    if (NS_SUCCEEDED(os->EnumerateObservers(TOPIC_PLACES_INIT_COMPLETE,
  1.1970 +                     getter_AddRefs(e))) && e) {
  1.1971 +      bool hasMore = false;
  1.1972 +      while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
  1.1973 +	nsCOMPtr<nsISupports> supports;
  1.1974 +        if (NS_SUCCEEDED(e->GetNext(getter_AddRefs(supports)))) {
  1.1975 +          nsCOMPtr<nsIObserver> observer = do_QueryInterface(supports);
  1.1976 +          (void)observer->Observe(observer, TOPIC_PLACES_INIT_COMPLETE, nullptr);
  1.1977 +        }
  1.1978 +      }
  1.1979 +    }
  1.1980 +
  1.1981 +    // Notify all Places users that we are about to shutdown.
  1.1982 +    (void)os->NotifyObservers(nullptr, TOPIC_PLACES_SHUTDOWN, nullptr);
  1.1983 +  }
  1.1984 +
  1.1985 +  else if (strcmp(aTopic, TOPIC_PROFILE_BEFORE_CHANGE) == 0) {
  1.1986 +    // Tests simulating shutdown may cause re-entrance.
  1.1987 +    if (mShuttingDown) {
  1.1988 +      return NS_OK;
  1.1989 +    }
  1.1990 +
  1.1991 +    // Fire internal shutdown notifications.
  1.1992 +    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  1.1993 +    if (os) {
  1.1994 +      (void)os->NotifyObservers(nullptr, TOPIC_PLACES_WILL_CLOSE_CONNECTION, nullptr);
  1.1995 +    }
  1.1996 +
  1.1997 +#ifdef DEBUG
  1.1998 +    { // Sanity check for missing guids.
  1.1999 +      bool haveNullGuids = false;
  1.2000 +      nsCOMPtr<mozIStorageStatement> stmt;
  1.2001 +
  1.2002 +      nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.2003 +        "SELECT 1 "
  1.2004 +        "FROM moz_places "
  1.2005 +        "WHERE guid IS NULL "
  1.2006 +      ), getter_AddRefs(stmt));
  1.2007 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2008 +      rv = stmt->ExecuteStep(&haveNullGuids);
  1.2009 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2010 +      MOZ_ASSERT(!haveNullGuids && "Found a page without a GUID!");
  1.2011 +
  1.2012 +      rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.2013 +        "SELECT 1 "
  1.2014 +        "FROM moz_bookmarks "
  1.2015 +        "WHERE guid IS NULL "
  1.2016 +      ), getter_AddRefs(stmt));
  1.2017 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2018 +      rv = stmt->ExecuteStep(&haveNullGuids);
  1.2019 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2020 +      MOZ_ASSERT(!haveNullGuids && "Found a bookmark without a GUID!");
  1.2021 +
  1.2022 +      rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
  1.2023 +        "SELECT 1 "
  1.2024 +        "FROM moz_favicons "
  1.2025 +        "WHERE guid IS NULL "
  1.2026 +      ), getter_AddRefs(stmt));
  1.2027 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2028 +      rv = stmt->ExecuteStep(&haveNullGuids);
  1.2029 +      NS_ENSURE_SUCCESS(rv, rv);
  1.2030 +      MOZ_ASSERT(!haveNullGuids && "Found a favicon without a GUID!");
  1.2031 +    }
  1.2032 +#endif
  1.2033 +
  1.2034 +    // As the last step in the shutdown path, finalize the database handle.
  1.2035 +    Shutdown();
  1.2036 +  }
  1.2037 +
  1.2038 +  return NS_OK;
  1.2039 +}
  1.2040 +
  1.2041 +} // namespace places
  1.2042 +} // namespace mozilla

mercurial