toolkit/components/places/Database.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "mozilla/ArrayUtils.h"
michael@0 6 #include "mozilla/Attributes.h"
michael@0 7 #include "mozilla/DebugOnly.h"
michael@0 8
michael@0 9 #include "Database.h"
michael@0 10
michael@0 11 #include "nsINavBookmarksService.h"
michael@0 12 #include "nsIInterfaceRequestorUtils.h"
michael@0 13 #include "nsIFile.h"
michael@0 14
michael@0 15 #include "nsNavHistory.h"
michael@0 16 #include "nsPlacesTables.h"
michael@0 17 #include "nsPlacesIndexes.h"
michael@0 18 #include "nsPlacesTriggers.h"
michael@0 19 #include "nsPlacesMacros.h"
michael@0 20 #include "SQLFunctions.h"
michael@0 21 #include "Helpers.h"
michael@0 22
michael@0 23 #include "nsAppDirectoryServiceDefs.h"
michael@0 24 #include "nsDirectoryServiceUtils.h"
michael@0 25 #include "prsystem.h"
michael@0 26 #include "nsPrintfCString.h"
michael@0 27 #include "mozilla/Preferences.h"
michael@0 28 #include "mozilla/Services.h"
michael@0 29 #include "prtime.h"
michael@0 30
michael@0 31 // Time between corrupt database backups.
michael@0 32 #define RECENT_BACKUP_TIME_MICROSEC (int64_t)86400 * PR_USEC_PER_SEC // 24H
michael@0 33
michael@0 34 // Filename of the database.
michael@0 35 #define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite")
michael@0 36 // Filename used to backup corrupt databases.
michael@0 37 #define DATABASE_CORRUPT_FILENAME NS_LITERAL_STRING("places.sqlite.corrupt")
michael@0 38
michael@0 39 // Set when the database file was found corrupt by a previous maintenance.
michael@0 40 #define PREF_FORCE_DATABASE_REPLACEMENT "places.database.replaceOnStartup"
michael@0 41
michael@0 42 // Set to specify the size of the places database growth increments in kibibytes
michael@0 43 #define PREF_GROWTH_INCREMENT_KIB "places.database.growthIncrementKiB"
michael@0 44
michael@0 45 // Maximum size for the WAL file. It should be small enough since in case of
michael@0 46 // crashes we could lose all the transactions in the file. But a too small
michael@0 47 // file could hurt performance.
michael@0 48 #define DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES 512
michael@0 49
michael@0 50 #define BYTES_PER_KIBIBYTE 1024
michael@0 51
michael@0 52 // Old Sync GUID annotation.
michael@0 53 #define SYNCGUID_ANNO NS_LITERAL_CSTRING("sync/guid")
michael@0 54
michael@0 55 // Places string bundle, contains internationalized bookmark root names.
michael@0 56 #define PLACES_BUNDLE "chrome://places/locale/places.properties"
michael@0 57
michael@0 58 // Livemarks annotations.
michael@0 59 #define LMANNO_FEEDURI "livemark/feedURI"
michael@0 60 #define LMANNO_SITEURI "livemark/siteURI"
michael@0 61
michael@0 62 using namespace mozilla;
michael@0 63
michael@0 64 namespace mozilla {
michael@0 65 namespace places {
michael@0 66
michael@0 67 namespace {
michael@0 68
michael@0 69 ////////////////////////////////////////////////////////////////////////////////
michael@0 70 //// Helpers
michael@0 71
michael@0 72 /**
michael@0 73 * Checks whether exists a database backup created not longer than
michael@0 74 * RECENT_BACKUP_TIME_MICROSEC ago.
michael@0 75 */
michael@0 76 bool
michael@0 77 hasRecentCorruptDB()
michael@0 78 {
michael@0 79 MOZ_ASSERT(NS_IsMainThread());
michael@0 80
michael@0 81 nsCOMPtr<nsIFile> profDir;
michael@0 82 NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profDir));
michael@0 83 NS_ENSURE_TRUE(profDir, false);
michael@0 84 nsCOMPtr<nsISimpleEnumerator> entries;
michael@0 85 profDir->GetDirectoryEntries(getter_AddRefs(entries));
michael@0 86 NS_ENSURE_TRUE(entries, false);
michael@0 87 bool hasMore;
michael@0 88 while (NS_SUCCEEDED(entries->HasMoreElements(&hasMore)) && hasMore) {
michael@0 89 nsCOMPtr<nsISupports> next;
michael@0 90 entries->GetNext(getter_AddRefs(next));
michael@0 91 NS_ENSURE_TRUE(next, false);
michael@0 92 nsCOMPtr<nsIFile> currFile = do_QueryInterface(next);
michael@0 93 NS_ENSURE_TRUE(currFile, false);
michael@0 94
michael@0 95 nsAutoString leafName;
michael@0 96 if (NS_SUCCEEDED(currFile->GetLeafName(leafName)) &&
michael@0 97 leafName.Length() >= DATABASE_CORRUPT_FILENAME.Length() &&
michael@0 98 leafName.Find(".corrupt", DATABASE_FILENAME.Length()) != -1) {
michael@0 99 PRTime lastMod = 0;
michael@0 100 currFile->GetLastModifiedTime(&lastMod);
michael@0 101 NS_ENSURE_TRUE(lastMod > 0, false);
michael@0 102 return (PR_Now() - lastMod) > RECENT_BACKUP_TIME_MICROSEC;
michael@0 103 }
michael@0 104 }
michael@0 105 return false;
michael@0 106 }
michael@0 107
michael@0 108 /**
michael@0 109 * Updates sqlite_stat1 table through ANALYZE.
michael@0 110 * Since also nsPlacesExpiration.js executes ANALYZE, the analyzed tables
michael@0 111 * must be the same in both components. So ensure they are in sync.
michael@0 112 *
michael@0 113 * @param aDBConn
michael@0 114 * The database connection.
michael@0 115 */
michael@0 116 nsresult
michael@0 117 updateSQLiteStatistics(mozIStorageConnection* aDBConn)
michael@0 118 {
michael@0 119 MOZ_ASSERT(NS_IsMainThread());
michael@0 120 nsCOMPtr<mozIStorageAsyncStatement> analyzePlacesStmt;
michael@0 121 aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 122 "ANALYZE moz_places"
michael@0 123 ), getter_AddRefs(analyzePlacesStmt));
michael@0 124 NS_ENSURE_STATE(analyzePlacesStmt);
michael@0 125 nsCOMPtr<mozIStorageAsyncStatement> analyzeBookmarksStmt;
michael@0 126 aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 127 "ANALYZE moz_bookmarks"
michael@0 128 ), getter_AddRefs(analyzeBookmarksStmt));
michael@0 129 NS_ENSURE_STATE(analyzeBookmarksStmt);
michael@0 130 nsCOMPtr<mozIStorageAsyncStatement> analyzeVisitsStmt;
michael@0 131 aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 132 "ANALYZE moz_historyvisits"
michael@0 133 ), getter_AddRefs(analyzeVisitsStmt));
michael@0 134 NS_ENSURE_STATE(analyzeVisitsStmt);
michael@0 135 nsCOMPtr<mozIStorageAsyncStatement> analyzeInputStmt;
michael@0 136 aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 137 "ANALYZE moz_inputhistory"
michael@0 138 ), getter_AddRefs(analyzeInputStmt));
michael@0 139 NS_ENSURE_STATE(analyzeInputStmt);
michael@0 140
michael@0 141 mozIStorageBaseStatement *stmts[] = {
michael@0 142 analyzePlacesStmt,
michael@0 143 analyzeBookmarksStmt,
michael@0 144 analyzeVisitsStmt,
michael@0 145 analyzeInputStmt
michael@0 146 };
michael@0 147
michael@0 148 nsCOMPtr<mozIStoragePendingStatement> ps;
michael@0 149 (void)aDBConn->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
michael@0 150 getter_AddRefs(ps));
michael@0 151 return NS_OK;
michael@0 152 }
michael@0 153
michael@0 154 /**
michael@0 155 * Sets the connection journal mode to one of the JOURNAL_* types.
michael@0 156 *
michael@0 157 * @param aDBConn
michael@0 158 * The database connection.
michael@0 159 * @param aJournalMode
michael@0 160 * One of the JOURNAL_* types.
michael@0 161 * @returns the current journal mode.
michael@0 162 * @note this may return a different journal mode than the required one, since
michael@0 163 * setting it may fail.
michael@0 164 */
michael@0 165 enum JournalMode
michael@0 166 SetJournalMode(nsCOMPtr<mozIStorageConnection>& aDBConn,
michael@0 167 enum JournalMode aJournalMode)
michael@0 168 {
michael@0 169 MOZ_ASSERT(NS_IsMainThread());
michael@0 170 nsAutoCString journalMode;
michael@0 171 switch (aJournalMode) {
michael@0 172 default:
michael@0 173 MOZ_ASSERT(false, "Trying to set an unknown journal mode.");
michael@0 174 // Fall through to the default DELETE journal.
michael@0 175 case JOURNAL_DELETE:
michael@0 176 journalMode.AssignLiteral("delete");
michael@0 177 break;
michael@0 178 case JOURNAL_TRUNCATE:
michael@0 179 journalMode.AssignLiteral("truncate");
michael@0 180 break;
michael@0 181 case JOURNAL_MEMORY:
michael@0 182 journalMode.AssignLiteral("memory");
michael@0 183 break;
michael@0 184 case JOURNAL_WAL:
michael@0 185 journalMode.AssignLiteral("wal");
michael@0 186 break;
michael@0 187 }
michael@0 188
michael@0 189 nsCOMPtr<mozIStorageStatement> statement;
michael@0 190 nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR
michael@0 191 "PRAGMA journal_mode = ");
michael@0 192 query.Append(journalMode);
michael@0 193 aDBConn->CreateStatement(query, getter_AddRefs(statement));
michael@0 194 NS_ENSURE_TRUE(statement, JOURNAL_DELETE);
michael@0 195
michael@0 196 bool hasResult = false;
michael@0 197 if (NS_SUCCEEDED(statement->ExecuteStep(&hasResult)) && hasResult &&
michael@0 198 NS_SUCCEEDED(statement->GetUTF8String(0, journalMode))) {
michael@0 199 if (journalMode.EqualsLiteral("delete")) {
michael@0 200 return JOURNAL_DELETE;
michael@0 201 }
michael@0 202 if (journalMode.EqualsLiteral("truncate")) {
michael@0 203 return JOURNAL_TRUNCATE;
michael@0 204 }
michael@0 205 if (journalMode.EqualsLiteral("memory")) {
michael@0 206 return JOURNAL_MEMORY;
michael@0 207 }
michael@0 208 if (journalMode.EqualsLiteral("wal")) {
michael@0 209 return JOURNAL_WAL;
michael@0 210 }
michael@0 211 // This is an unknown journal.
michael@0 212 MOZ_ASSERT(true);
michael@0 213 }
michael@0 214
michael@0 215 return JOURNAL_DELETE;
michael@0 216 }
michael@0 217
michael@0 218 class ConnectionCloseCallback MOZ_FINAL : public mozIStorageCompletionCallback {
michael@0 219 bool mDone;
michael@0 220
michael@0 221 public:
michael@0 222 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 223 NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
michael@0 224 ConnectionCloseCallback();
michael@0 225 };
michael@0 226
michael@0 227 NS_IMETHODIMP
michael@0 228 ConnectionCloseCallback::Complete(nsresult, nsISupports*)
michael@0 229 {
michael@0 230 mDone = true;
michael@0 231 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
michael@0 232 MOZ_ASSERT(os);
michael@0 233 if (!os)
michael@0 234 return NS_OK;
michael@0 235 DebugOnly<nsresult> rv = os->NotifyObservers(nullptr,
michael@0 236 TOPIC_PLACES_CONNECTION_CLOSED,
michael@0 237 nullptr);
michael@0 238 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 239 return NS_OK;
michael@0 240 }
michael@0 241
michael@0 242 ConnectionCloseCallback::ConnectionCloseCallback()
michael@0 243 : mDone(false)
michael@0 244 {
michael@0 245 MOZ_ASSERT(NS_IsMainThread());
michael@0 246 }
michael@0 247
michael@0 248 NS_IMPL_ISUPPORTS(
michael@0 249 ConnectionCloseCallback
michael@0 250 , mozIStorageCompletionCallback
michael@0 251 )
michael@0 252
michael@0 253 nsresult
michael@0 254 CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
michael@0 255 const nsCString& aRootName,
michael@0 256 const nsXPIDLString& titleString)
michael@0 257 {
michael@0 258 MOZ_ASSERT(NS_IsMainThread());
michael@0 259
michael@0 260 // The position of the new item in its folder.
michael@0 261 static int32_t itemPosition = 0;
michael@0 262
michael@0 263 // A single creation timestamp for all roots so that the root folder's
michael@0 264 // last modification time isn't earlier than its childrens' creation time.
michael@0 265 static PRTime timestamp = 0;
michael@0 266 if (!timestamp)
michael@0 267 timestamp = PR_Now();
michael@0 268
michael@0 269 // Create a new bookmark folder for the root.
michael@0 270 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 271 nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 272 "INSERT INTO moz_bookmarks "
michael@0 273 "(type, position, title, dateAdded, lastModified, guid, parent) "
michael@0 274 "VALUES (:item_type, :item_position, :item_title,"
michael@0 275 ":date_added, :last_modified, GENERATE_GUID(),"
michael@0 276 "IFNULL((SELECT id FROM moz_bookmarks WHERE parent = 0), 0))"
michael@0 277 ), getter_AddRefs(stmt));
michael@0 278 if (NS_FAILED(rv)) return rv;
michael@0 279
michael@0 280 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"),
michael@0 281 nsINavBookmarksService::TYPE_FOLDER);
michael@0 282 if (NS_FAILED(rv)) return rv;
michael@0 283 rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"), itemPosition);
michael@0 284 if (NS_FAILED(rv)) return rv;
michael@0 285 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
michael@0 286 NS_ConvertUTF16toUTF8(titleString));
michael@0 287 if (NS_FAILED(rv)) return rv;
michael@0 288 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), timestamp);
michael@0 289 if (NS_FAILED(rv)) return rv;
michael@0 290 rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), timestamp);
michael@0 291 if (NS_FAILED(rv)) return rv;
michael@0 292 rv = stmt->Execute();
michael@0 293 if (NS_FAILED(rv)) return rv;
michael@0 294
michael@0 295 // Create an entry in moz_bookmarks_roots to link the folder to the root.
michael@0 296 nsCOMPtr<mozIStorageStatement> newRootStmt;
michael@0 297 rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 298 "INSERT INTO moz_bookmarks_roots (root_name, folder_id) "
michael@0 299 "VALUES (:root_name, "
michael@0 300 "(SELECT id from moz_bookmarks WHERE "
michael@0 301 " position = :item_position AND "
michael@0 302 " parent = IFNULL((SELECT MIN(folder_id) FROM moz_bookmarks_roots), 0)))"
michael@0 303 ), getter_AddRefs(newRootStmt));
michael@0 304 if (NS_FAILED(rv)) return rv;
michael@0 305
michael@0 306 rv = newRootStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"),
michael@0 307 aRootName);
michael@0 308 if (NS_FAILED(rv)) return rv;
michael@0 309 rv = newRootStmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"),
michael@0 310 itemPosition);
michael@0 311 if (NS_FAILED(rv)) return rv;
michael@0 312 rv = newRootStmt->Execute();
michael@0 313 if (NS_FAILED(rv)) return rv;
michael@0 314
michael@0 315 // The 'places' root is a folder containing the other roots.
michael@0 316 // The first bookmark in a folder has position 0.
michael@0 317 if (!aRootName.Equals("places"))
michael@0 318 ++itemPosition;
michael@0 319
michael@0 320 return NS_OK;
michael@0 321 }
michael@0 322
michael@0 323
michael@0 324 } // Anonymous namespace
michael@0 325
michael@0 326 ////////////////////////////////////////////////////////////////////////////////
michael@0 327 //// Database
michael@0 328
michael@0 329 PLACES_FACTORY_SINGLETON_IMPLEMENTATION(Database, gDatabase)
michael@0 330
michael@0 331 NS_IMPL_ISUPPORTS(Database
michael@0 332 , nsIObserver
michael@0 333 , nsISupportsWeakReference
michael@0 334 )
michael@0 335
michael@0 336 Database::Database()
michael@0 337 : mMainThreadStatements(mMainConn)
michael@0 338 , mMainThreadAsyncStatements(mMainConn)
michael@0 339 , mAsyncThreadStatements(mMainConn)
michael@0 340 , mDBPageSize(0)
michael@0 341 , mDatabaseStatus(nsINavHistoryService::DATABASE_STATUS_OK)
michael@0 342 , mShuttingDown(false)
michael@0 343 , mClosed(false)
michael@0 344 {
michael@0 345 // Attempting to create two instances of the service?
michael@0 346 MOZ_ASSERT(!gDatabase);
michael@0 347 gDatabase = this;
michael@0 348 }
michael@0 349
michael@0 350 Database::~Database()
michael@0 351 {
michael@0 352 // Check to make sure it's us, in case somebody wrongly creates an extra
michael@0 353 // instance of this singleton class.
michael@0 354 MOZ_ASSERT(gDatabase == this);
michael@0 355
michael@0 356 // Remove the static reference to the service.
michael@0 357 if (gDatabase == this) {
michael@0 358 gDatabase = nullptr;
michael@0 359 }
michael@0 360 }
michael@0 361
michael@0 362 nsresult
michael@0 363 Database::Init()
michael@0 364 {
michael@0 365 MOZ_ASSERT(NS_IsMainThread());
michael@0 366
michael@0 367 nsCOMPtr<mozIStorageService> storage =
michael@0 368 do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
michael@0 369 NS_ENSURE_STATE(storage);
michael@0 370
michael@0 371 // Init the database file and connect to it.
michael@0 372 bool databaseCreated = false;
michael@0 373 nsresult rv = InitDatabaseFile(storage, &databaseCreated);
michael@0 374 if (NS_SUCCEEDED(rv) && databaseCreated) {
michael@0 375 mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CREATE;
michael@0 376 }
michael@0 377 else if (rv == NS_ERROR_FILE_CORRUPTED) {
michael@0 378 // The database is corrupt, backup and replace it with a new one.
michael@0 379 mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CORRUPT;
michael@0 380 rv = BackupAndReplaceDatabaseFile(storage);
michael@0 381 // Fallback to catch-all handler, that notifies a database locked failure.
michael@0 382 }
michael@0 383
michael@0 384 // If the database connection still cannot be opened, it may just be locked
michael@0 385 // by third parties. Send out a notification and interrupt initialization.
michael@0 386 if (NS_FAILED(rv)) {
michael@0 387 nsRefPtr<PlacesEvent> lockedEvent = new PlacesEvent(TOPIC_DATABASE_LOCKED);
michael@0 388 (void)NS_DispatchToMainThread(lockedEvent);
michael@0 389 return rv;
michael@0 390 }
michael@0 391
michael@0 392 // Initialize the database schema. In case of failure the existing schema is
michael@0 393 // is corrupt or incoherent, thus the database should be replaced.
michael@0 394 bool databaseMigrated = false;
michael@0 395 rv = InitSchema(&databaseMigrated);
michael@0 396 if (NS_FAILED(rv)) {
michael@0 397 mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CORRUPT;
michael@0 398 rv = BackupAndReplaceDatabaseFile(storage);
michael@0 399 NS_ENSURE_SUCCESS(rv, rv);
michael@0 400 // Try to initialize the schema again on the new database.
michael@0 401 rv = InitSchema(&databaseMigrated);
michael@0 402 NS_ENSURE_SUCCESS(rv, rv);
michael@0 403 }
michael@0 404
michael@0 405 if (databaseMigrated) {
michael@0 406 mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_UPGRADED;
michael@0 407 }
michael@0 408
michael@0 409 if (mDatabaseStatus != nsINavHistoryService::DATABASE_STATUS_OK) {
michael@0 410 rv = updateSQLiteStatistics(MainConn());
michael@0 411 NS_ENSURE_SUCCESS(rv, rv);
michael@0 412 }
michael@0 413
michael@0 414 // Initialize here all the items that are not part of the on-disk database,
michael@0 415 // like views, temp triggers or temp tables. The database should not be
michael@0 416 // considered corrupt if any of the following fails.
michael@0 417
michael@0 418 rv = InitTempTriggers();
michael@0 419 NS_ENSURE_SUCCESS(rv, rv);
michael@0 420
michael@0 421 // Notify we have finished database initialization.
michael@0 422 // Enqueue the notification, so if we init another service that requires
michael@0 423 // nsNavHistoryService we don't recursive try to get it.
michael@0 424 nsRefPtr<PlacesEvent> completeEvent =
michael@0 425 new PlacesEvent(TOPIC_PLACES_INIT_COMPLETE);
michael@0 426 rv = NS_DispatchToMainThread(completeEvent);
michael@0 427 NS_ENSURE_SUCCESS(rv, rv);
michael@0 428
michael@0 429 // Finally observe profile shutdown notifications.
michael@0 430 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
michael@0 431 if (os) {
michael@0 432 (void)os->AddObserver(this, TOPIC_PROFILE_CHANGE_TEARDOWN, true);
michael@0 433 (void)os->AddObserver(this, TOPIC_PROFILE_BEFORE_CHANGE, true);
michael@0 434 }
michael@0 435
michael@0 436 return NS_OK;
michael@0 437 }
michael@0 438
michael@0 439 nsresult
michael@0 440 Database::InitDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage,
michael@0 441 bool* aNewDatabaseCreated)
michael@0 442 {
michael@0 443 MOZ_ASSERT(NS_IsMainThread());
michael@0 444 *aNewDatabaseCreated = false;
michael@0 445
michael@0 446 nsCOMPtr<nsIFile> databaseFile;
michael@0 447 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
michael@0 448 getter_AddRefs(databaseFile));
michael@0 449 NS_ENSURE_SUCCESS(rv, rv);
michael@0 450 rv = databaseFile->Append(DATABASE_FILENAME);
michael@0 451 NS_ENSURE_SUCCESS(rv, rv);
michael@0 452
michael@0 453 bool databaseFileExists = false;
michael@0 454 rv = databaseFile->Exists(&databaseFileExists);
michael@0 455 NS_ENSURE_SUCCESS(rv, rv);
michael@0 456
michael@0 457 if (databaseFileExists &&
michael@0 458 Preferences::GetBool(PREF_FORCE_DATABASE_REPLACEMENT, false)) {
michael@0 459 // If this pref is set, Maintenance required a database replacement, due to
michael@0 460 // integrity corruption.
michael@0 461 // Be sure to clear the pref to avoid handling it more than once.
michael@0 462 (void)Preferences::ClearUser(PREF_FORCE_DATABASE_REPLACEMENT);
michael@0 463
michael@0 464 return NS_ERROR_FILE_CORRUPTED;
michael@0 465 }
michael@0 466
michael@0 467 // Open the database file. If it does not exist a new one will be created.
michael@0 468 // Use an unshared connection, it will consume more memory but avoid shared
michael@0 469 // cache contentions across threads.
michael@0 470 rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
michael@0 471 NS_ENSURE_SUCCESS(rv, rv);
michael@0 472
michael@0 473 *aNewDatabaseCreated = !databaseFileExists;
michael@0 474 return NS_OK;
michael@0 475 }
michael@0 476
michael@0 477 nsresult
michael@0 478 Database::BackupAndReplaceDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage)
michael@0 479 {
michael@0 480 MOZ_ASSERT(NS_IsMainThread());
michael@0 481 nsCOMPtr<nsIFile> profDir;
michael@0 482 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
michael@0 483 getter_AddRefs(profDir));
michael@0 484 NS_ENSURE_SUCCESS(rv, rv);
michael@0 485 nsCOMPtr<nsIFile> databaseFile;
michael@0 486 rv = profDir->Clone(getter_AddRefs(databaseFile));
michael@0 487 NS_ENSURE_SUCCESS(rv, rv);
michael@0 488 rv = databaseFile->Append(DATABASE_FILENAME);
michael@0 489 NS_ENSURE_SUCCESS(rv, rv);
michael@0 490
michael@0 491 // If we have
michael@0 492 // already failed in the last 24 hours avoid to create another corrupt file,
michael@0 493 // since doing so, in some situation, could cause us to create a new corrupt
michael@0 494 // file at every try to access any Places service. That is bad because it
michael@0 495 // would quickly fill the user's disk space without any notice.
michael@0 496 if (!hasRecentCorruptDB()) {
michael@0 497 nsCOMPtr<nsIFile> backup;
michael@0 498 (void)aStorage->BackupDatabaseFile(databaseFile, DATABASE_CORRUPT_FILENAME,
michael@0 499 profDir, getter_AddRefs(backup));
michael@0 500 }
michael@0 501
michael@0 502 // Close database connection if open.
michael@0 503 if (mMainConn) {
michael@0 504 // If there's any not finalized statement or this fails for any reason
michael@0 505 // we won't be able to remove the database.
michael@0 506 rv = mMainConn->Close();
michael@0 507 NS_ENSURE_SUCCESS(rv, rv);
michael@0 508 }
michael@0 509
michael@0 510 // Remove the broken database.
michael@0 511 rv = databaseFile->Remove(false);
michael@0 512 NS_ENSURE_SUCCESS(rv, rv);
michael@0 513
michael@0 514 // Create a new database file.
michael@0 515 // Use an unshared connection, it will consume more memory but avoid shared
michael@0 516 // cache contentions across threads.
michael@0 517 rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
michael@0 518 NS_ENSURE_SUCCESS(rv, rv);
michael@0 519
michael@0 520 return NS_OK;
michael@0 521 }
michael@0 522
michael@0 523 nsresult
michael@0 524 Database::InitSchema(bool* aDatabaseMigrated)
michael@0 525 {
michael@0 526 MOZ_ASSERT(NS_IsMainThread());
michael@0 527 *aDatabaseMigrated = false;
michael@0 528
michael@0 529 // WARNING: any statement executed before setting the journal mode must be
michael@0 530 // finalized, since SQLite doesn't allow changing the journal mode if there
michael@0 531 // is any outstanding statement.
michael@0 532
michael@0 533 {
michael@0 534 // Get the page size. This may be different than the default if the
michael@0 535 // database file already existed with a different page size.
michael@0 536 nsCOMPtr<mozIStorageStatement> statement;
michael@0 537 nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 538 MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"
michael@0 539 ), getter_AddRefs(statement));
michael@0 540 NS_ENSURE_SUCCESS(rv, rv);
michael@0 541 bool hasResult = false;
michael@0 542 rv = statement->ExecuteStep(&hasResult);
michael@0 543 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_FAILURE);
michael@0 544 rv = statement->GetInt32(0, &mDBPageSize);
michael@0 545 NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && mDBPageSize > 0, NS_ERROR_UNEXPECTED);
michael@0 546 }
michael@0 547
michael@0 548 // Ensure that temp tables are held in memory, not on disk.
michael@0 549 nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 550 MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA temp_store = MEMORY"));
michael@0 551 NS_ENSURE_SUCCESS(rv, rv);
michael@0 552
michael@0 553 // Be sure to set journal mode after page_size. WAL would prevent the change
michael@0 554 // otherwise.
michael@0 555 if (JOURNAL_WAL == SetJournalMode(mMainConn, JOURNAL_WAL)) {
michael@0 556 // Set the WAL journal size limit. We want it to be small, since in
michael@0 557 // synchronous = NORMAL mode a crash could cause loss of all the
michael@0 558 // transactions in the journal. For added safety we will also force
michael@0 559 // checkpointing at strategic moments.
michael@0 560 int32_t checkpointPages =
michael@0 561 static_cast<int32_t>(DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES * 1024 / mDBPageSize);
michael@0 562 nsAutoCString checkpointPragma("PRAGMA wal_autocheckpoint = ");
michael@0 563 checkpointPragma.AppendInt(checkpointPages);
michael@0 564 rv = mMainConn->ExecuteSimpleSQL(checkpointPragma);
michael@0 565 NS_ENSURE_SUCCESS(rv, rv);
michael@0 566 }
michael@0 567 else {
michael@0 568 // Ignore errors, if we fail here the database could be considered corrupt
michael@0 569 // and we won't be able to go on, even if it's just matter of a bogus file
michael@0 570 // system. The default mode (DELETE) will be fine in such a case.
michael@0 571 (void)SetJournalMode(mMainConn, JOURNAL_TRUNCATE);
michael@0 572
michael@0 573 // Set synchronous to FULL to ensure maximum data integrity, even in
michael@0 574 // case of crashes or unclean shutdowns.
michael@0 575 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 576 "PRAGMA synchronous = FULL"));
michael@0 577 NS_ENSURE_SUCCESS(rv, rv);
michael@0 578 }
michael@0 579
michael@0 580 // The journal is usually free to grow for performance reasons, but it never
michael@0 581 // shrinks back. Since the space taken may be problematic, especially on
michael@0 582 // mobile devices, limit its size.
michael@0 583 // Since exceeding the limit will cause a truncate, allow a slightly
michael@0 584 // larger limit than DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES to reduce the number
michael@0 585 // of times it is needed.
michael@0 586 nsAutoCString journalSizePragma("PRAGMA journal_size_limit = ");
michael@0 587 journalSizePragma.AppendInt(DATABASE_MAX_WAL_SIZE_IN_KIBIBYTES * 3);
michael@0 588 (void)mMainConn->ExecuteSimpleSQL(journalSizePragma);
michael@0 589
michael@0 590 // Grow places in |growthIncrementKiB| increments to limit fragmentation on disk.
michael@0 591 // By default, it's 10 MB.
michael@0 592 int32_t growthIncrementKiB =
michael@0 593 Preferences::GetInt(PREF_GROWTH_INCREMENT_KIB, 10 * BYTES_PER_KIBIBYTE);
michael@0 594 if (growthIncrementKiB > 0) {
michael@0 595 (void)mMainConn->SetGrowthIncrement(growthIncrementKiB * BYTES_PER_KIBIBYTE, EmptyCString());
michael@0 596 }
michael@0 597
michael@0 598 // We use our functions during migration, so initialize them now.
michael@0 599 rv = InitFunctions();
michael@0 600 NS_ENSURE_SUCCESS(rv, rv);
michael@0 601
michael@0 602 // Get the database schema version.
michael@0 603 int32_t currentSchemaVersion;
michael@0 604 rv = mMainConn->GetSchemaVersion(&currentSchemaVersion);
michael@0 605 NS_ENSURE_SUCCESS(rv, rv);
michael@0 606 bool databaseInitialized = currentSchemaVersion > 0;
michael@0 607
michael@0 608 if (databaseInitialized && currentSchemaVersion == DATABASE_SCHEMA_VERSION) {
michael@0 609 // The database is up to date and ready to go.
michael@0 610 return NS_OK;
michael@0 611 }
michael@0 612
michael@0 613 // We are going to update the database, so everything from now on should be in
michael@0 614 // a transaction for performances.
michael@0 615 mozStorageTransaction transaction(mMainConn, false);
michael@0 616
michael@0 617 if (databaseInitialized) {
michael@0 618 // Migration How-to:
michael@0 619 //
michael@0 620 // 1. increment PLACES_SCHEMA_VERSION.
michael@0 621 // 2. implement a method that performs upgrade to your version from the
michael@0 622 // previous one.
michael@0 623 //
michael@0 624 // NOTE: The downgrade process is pretty much complicated by the fact old
michael@0 625 // versions cannot know what a new version is going to implement.
michael@0 626 // The only thing we will do for downgrades is setting back the schema
michael@0 627 // version, so that next upgrades will run again the migration step.
michael@0 628
michael@0 629 if (currentSchemaVersion < DATABASE_SCHEMA_VERSION) {
michael@0 630 *aDatabaseMigrated = true;
michael@0 631
michael@0 632 if (currentSchemaVersion < 6) {
michael@0 633 // These are early Firefox 3.0 alpha versions that are not supported
michael@0 634 // anymore. In this case it's safer to just replace the database.
michael@0 635 return NS_ERROR_FILE_CORRUPTED;
michael@0 636 }
michael@0 637
michael@0 638 // Firefox 3.0 uses schema version 6.
michael@0 639
michael@0 640 if (currentSchemaVersion < 7) {
michael@0 641 rv = MigrateV7Up();
michael@0 642 NS_ENSURE_SUCCESS(rv, rv);
michael@0 643 }
michael@0 644
michael@0 645 if (currentSchemaVersion < 8) {
michael@0 646 rv = MigrateV8Up();
michael@0 647 NS_ENSURE_SUCCESS(rv, rv);
michael@0 648 }
michael@0 649
michael@0 650 // Firefox 3.5 uses schema version 8.
michael@0 651
michael@0 652 if (currentSchemaVersion < 9) {
michael@0 653 rv = MigrateV9Up();
michael@0 654 NS_ENSURE_SUCCESS(rv, rv);
michael@0 655 }
michael@0 656
michael@0 657 if (currentSchemaVersion < 10) {
michael@0 658 rv = MigrateV10Up();
michael@0 659 NS_ENSURE_SUCCESS(rv, rv);
michael@0 660 }
michael@0 661
michael@0 662 // Firefox 3.6 uses schema version 10.
michael@0 663
michael@0 664 if (currentSchemaVersion < 11) {
michael@0 665 rv = MigrateV11Up();
michael@0 666 NS_ENSURE_SUCCESS(rv, rv);
michael@0 667 }
michael@0 668
michael@0 669 // Firefox 4 uses schema version 11.
michael@0 670
michael@0 671 // Firefox 8 uses schema version 12.
michael@0 672
michael@0 673 if (currentSchemaVersion < 13) {
michael@0 674 rv = MigrateV13Up();
michael@0 675 NS_ENSURE_SUCCESS(rv, rv);
michael@0 676 }
michael@0 677
michael@0 678 if (currentSchemaVersion < 14) {
michael@0 679 rv = MigrateV14Up();
michael@0 680 NS_ENSURE_SUCCESS(rv, rv);
michael@0 681 }
michael@0 682
michael@0 683 if (currentSchemaVersion < 15) {
michael@0 684 rv = MigrateV15Up();
michael@0 685 NS_ENSURE_SUCCESS(rv, rv);
michael@0 686 }
michael@0 687
michael@0 688 if (currentSchemaVersion < 16) {
michael@0 689 rv = MigrateV16Up();
michael@0 690 NS_ENSURE_SUCCESS(rv, rv);
michael@0 691 }
michael@0 692
michael@0 693 // Firefox 11 uses schema version 16.
michael@0 694
michael@0 695 if (currentSchemaVersion < 17) {
michael@0 696 rv = MigrateV17Up();
michael@0 697 NS_ENSURE_SUCCESS(rv, rv);
michael@0 698 }
michael@0 699
michael@0 700 // Firefox 12 uses schema version 17.
michael@0 701
michael@0 702 if (currentSchemaVersion < 18) {
michael@0 703 rv = MigrateV18Up();
michael@0 704 NS_ENSURE_SUCCESS(rv, rv);
michael@0 705 }
michael@0 706
michael@0 707 if (currentSchemaVersion < 19) {
michael@0 708 rv = MigrateV19Up();
michael@0 709 NS_ENSURE_SUCCESS(rv, rv);
michael@0 710 }
michael@0 711
michael@0 712 // Firefox 13 uses schema version 19.
michael@0 713
michael@0 714 if (currentSchemaVersion < 20) {
michael@0 715 rv = MigrateV20Up();
michael@0 716 NS_ENSURE_SUCCESS(rv, rv);
michael@0 717 }
michael@0 718
michael@0 719 if (currentSchemaVersion < 21) {
michael@0 720 rv = MigrateV21Up();
michael@0 721 NS_ENSURE_SUCCESS(rv, rv);
michael@0 722 }
michael@0 723
michael@0 724 // Firefox 14 uses schema version 21.
michael@0 725
michael@0 726 if (currentSchemaVersion < 22) {
michael@0 727 rv = MigrateV22Up();
michael@0 728 NS_ENSURE_SUCCESS(rv, rv);
michael@0 729 }
michael@0 730
michael@0 731 // Firefox 22 uses schema version 22.
michael@0 732
michael@0 733 if (currentSchemaVersion < 23) {
michael@0 734 rv = MigrateV23Up();
michael@0 735 NS_ENSURE_SUCCESS(rv, rv);
michael@0 736 }
michael@0 737
michael@0 738 // Firefox 24 uses schema version 23.
michael@0 739
michael@0 740 // Schema Upgrades must add migration code here.
michael@0 741
michael@0 742 rv = UpdateBookmarkRootTitles();
michael@0 743 // We don't want a broken localization to cause us to think
michael@0 744 // the database is corrupt and needs to be replaced.
michael@0 745 MOZ_ASSERT(NS_SUCCEEDED(rv));
michael@0 746 }
michael@0 747 }
michael@0 748 else {
michael@0 749 // This is a new database, so we have to create all the tables and indices.
michael@0 750
michael@0 751 // moz_places.
michael@0 752 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES);
michael@0 753 NS_ENSURE_SUCCESS(rv, rv);
michael@0 754 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL);
michael@0 755 NS_ENSURE_SUCCESS(rv, rv);
michael@0 756 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FAVICON);
michael@0 757 NS_ENSURE_SUCCESS(rv, rv);
michael@0 758 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_REVHOST);
michael@0 759 NS_ENSURE_SUCCESS(rv, rv);
michael@0 760 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_VISITCOUNT);
michael@0 761 NS_ENSURE_SUCCESS(rv, rv);
michael@0 762 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FRECENCY);
michael@0 763 NS_ENSURE_SUCCESS(rv, rv);
michael@0 764 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_LASTVISITDATE);
michael@0 765 NS_ENSURE_SUCCESS(rv, rv);
michael@0 766 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
michael@0 767 NS_ENSURE_SUCCESS(rv, rv);
michael@0 768
michael@0 769 // moz_historyvisits.
michael@0 770 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS);
michael@0 771 NS_ENSURE_SUCCESS(rv, rv);
michael@0 772 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_PLACEDATE);
michael@0 773 NS_ENSURE_SUCCESS(rv, rv);
michael@0 774 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_FROMVISIT);
michael@0 775 NS_ENSURE_SUCCESS(rv, rv);
michael@0 776 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_VISITDATE);
michael@0 777 NS_ENSURE_SUCCESS(rv, rv);
michael@0 778
michael@0 779 // moz_inputhistory.
michael@0 780 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_INPUTHISTORY);
michael@0 781 NS_ENSURE_SUCCESS(rv, rv);
michael@0 782
michael@0 783 // moz_hosts.
michael@0 784 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS);
michael@0 785 NS_ENSURE_SUCCESS(rv, rv);
michael@0 786
michael@0 787 // moz_bookmarks.
michael@0 788 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS);
michael@0 789 NS_ENSURE_SUCCESS(rv, rv);
michael@0 790 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACETYPE);
michael@0 791 NS_ENSURE_SUCCESS(rv, rv);
michael@0 792 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PARENTPOSITION);
michael@0 793 NS_ENSURE_SUCCESS(rv, rv);
michael@0 794 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
michael@0 795 NS_ENSURE_SUCCESS(rv, rv);
michael@0 796 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
michael@0 797 NS_ENSURE_SUCCESS(rv, rv);
michael@0 798
michael@0 799 // moz_bookmarks_roots.
michael@0 800 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_ROOTS);
michael@0 801 NS_ENSURE_SUCCESS(rv, rv);
michael@0 802
michael@0 803 // moz_keywords.
michael@0 804 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_KEYWORDS);
michael@0 805 NS_ENSURE_SUCCESS(rv, rv);
michael@0 806
michael@0 807 // moz_favicons.
michael@0 808 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_FAVICONS);
michael@0 809 NS_ENSURE_SUCCESS(rv, rv);
michael@0 810
michael@0 811 // moz_anno_attributes.
michael@0 812 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ANNO_ATTRIBUTES);
michael@0 813 NS_ENSURE_SUCCESS(rv, rv);
michael@0 814
michael@0 815 // moz_annos.
michael@0 816 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ANNOS);
michael@0 817 NS_ENSURE_SUCCESS(rv, rv);
michael@0 818 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ANNOS_PLACEATTRIBUTE);
michael@0 819 NS_ENSURE_SUCCESS(rv, rv);
michael@0 820
michael@0 821 // moz_items_annos.
michael@0 822 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ITEMS_ANNOS);
michael@0 823 NS_ENSURE_SUCCESS(rv, rv);
michael@0 824 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
michael@0 825 NS_ENSURE_SUCCESS(rv, rv);
michael@0 826
michael@0 827 // Initialize the bookmark roots in the new DB.
michael@0 828 rv = CreateBookmarkRoots();
michael@0 829 NS_ENSURE_SUCCESS(rv, rv);
michael@0 830 }
michael@0 831
michael@0 832 // Set the schema version to the current one.
michael@0 833 rv = mMainConn->SetSchemaVersion(DATABASE_SCHEMA_VERSION);
michael@0 834 NS_ENSURE_SUCCESS(rv, rv);
michael@0 835
michael@0 836 rv = transaction.Commit();
michael@0 837 NS_ENSURE_SUCCESS(rv, rv);
michael@0 838
michael@0 839 ForceWALCheckpoint();
michael@0 840
michael@0 841 // ANY FAILURE IN THIS METHOD WILL CAUSE US TO MARK THE DATABASE AS CORRUPT
michael@0 842 // AND TRY TO REPLACE IT.
michael@0 843 // DO NOT PUT HERE ANYTHING THAT IS NOT RELATED TO INITIALIZATION OR MODIFYING
michael@0 844 // THE DISK DATABASE.
michael@0 845
michael@0 846 return NS_OK;
michael@0 847 }
michael@0 848
michael@0 849 nsresult
michael@0 850 Database::CreateBookmarkRoots()
michael@0 851 {
michael@0 852 MOZ_ASSERT(NS_IsMainThread());
michael@0 853
michael@0 854 nsCOMPtr<nsIStringBundleService> bundleService =
michael@0 855 services::GetStringBundleService();
michael@0 856 NS_ENSURE_STATE(bundleService);
michael@0 857 nsCOMPtr<nsIStringBundle> bundle;
michael@0 858 nsresult rv = bundleService->CreateBundle(PLACES_BUNDLE, getter_AddRefs(bundle));
michael@0 859 if (NS_FAILED(rv)) return rv;
michael@0 860
michael@0 861 nsXPIDLString rootTitle;
michael@0 862 // The first root's title is an empty string.
michael@0 863 rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("places"), rootTitle);
michael@0 864 if (NS_FAILED(rv)) return rv;
michael@0 865
michael@0 866 // Fetch the internationalized folder name from the string bundle.
michael@0 867 rv = bundle->GetStringFromName(MOZ_UTF16("BookmarksMenuFolderTitle"),
michael@0 868 getter_Copies(rootTitle));
michael@0 869 if (NS_FAILED(rv)) return rv;
michael@0 870 rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("menu"), rootTitle);
michael@0 871 if (NS_FAILED(rv)) return rv;
michael@0 872
michael@0 873 rv = bundle->GetStringFromName(MOZ_UTF16("BookmarksToolbarFolderTitle"),
michael@0 874 getter_Copies(rootTitle));
michael@0 875 if (NS_FAILED(rv)) return rv;
michael@0 876 rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("toolbar"), rootTitle);
michael@0 877 if (NS_FAILED(rv)) return rv;
michael@0 878
michael@0 879 rv = bundle->GetStringFromName(MOZ_UTF16("TagsFolderTitle"),
michael@0 880 getter_Copies(rootTitle));
michael@0 881 if (NS_FAILED(rv)) return rv;
michael@0 882 rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("tags"), rootTitle);
michael@0 883 if (NS_FAILED(rv)) return rv;
michael@0 884
michael@0 885 rv = bundle->GetStringFromName(MOZ_UTF16("UnsortedBookmarksFolderTitle"),
michael@0 886 getter_Copies(rootTitle));
michael@0 887 if (NS_FAILED(rv)) return rv;
michael@0 888 rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("unfiled"), rootTitle);
michael@0 889 if (NS_FAILED(rv)) return rv;
michael@0 890
michael@0 891 #if DEBUG
michael@0 892 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 893 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 894 "SELECT "
michael@0 895 "(SELECT COUNT(*) FROM moz_bookmarks), "
michael@0 896 "(SELECT COUNT(*) FROM moz_bookmarks_roots), "
michael@0 897 "(SELECT SUM(position) FROM moz_bookmarks WHERE "
michael@0 898 "id IN (SELECT folder_id FROM moz_bookmarks_roots))"
michael@0 899 ), getter_AddRefs(stmt));
michael@0 900 if (NS_FAILED(rv)) return rv;
michael@0 901
michael@0 902 bool hasResult;
michael@0 903 rv = stmt->ExecuteStep(&hasResult);
michael@0 904 if (NS_FAILED(rv)) return rv;
michael@0 905 MOZ_ASSERT(hasResult);
michael@0 906 int32_t bookmarkCount = 0;
michael@0 907 rv = stmt->GetInt32(0, &bookmarkCount);
michael@0 908 if (NS_FAILED(rv)) return rv;
michael@0 909 int32_t rootCount = 0;
michael@0 910 rv = stmt->GetInt32(1, &rootCount);
michael@0 911 if (NS_FAILED(rv)) return rv;
michael@0 912 int32_t positionSum = 0;
michael@0 913 rv = stmt->GetInt32(2, &positionSum);
michael@0 914 if (NS_FAILED(rv)) return rv;
michael@0 915 MOZ_ASSERT(bookmarkCount == 5 && rootCount == 5 && positionSum == 6);
michael@0 916 #endif
michael@0 917
michael@0 918 return NS_OK;
michael@0 919 }
michael@0 920
michael@0 921 nsresult
michael@0 922 Database::InitFunctions()
michael@0 923 {
michael@0 924 MOZ_ASSERT(NS_IsMainThread());
michael@0 925
michael@0 926 nsresult rv = GetUnreversedHostFunction::create(mMainConn);
michael@0 927 NS_ENSURE_SUCCESS(rv, rv);
michael@0 928 rv = MatchAutoCompleteFunction::create(mMainConn);
michael@0 929 NS_ENSURE_SUCCESS(rv, rv);
michael@0 930 rv = CalculateFrecencyFunction::create(mMainConn);
michael@0 931 NS_ENSURE_SUCCESS(rv, rv);
michael@0 932 rv = GenerateGUIDFunction::create(mMainConn);
michael@0 933 NS_ENSURE_SUCCESS(rv, rv);
michael@0 934 rv = FixupURLFunction::create(mMainConn);
michael@0 935 NS_ENSURE_SUCCESS(rv, rv);
michael@0 936 rv = FrecencyNotificationFunction::create(mMainConn);
michael@0 937 NS_ENSURE_SUCCESS(rv, rv);
michael@0 938
michael@0 939 return NS_OK;
michael@0 940 }
michael@0 941
michael@0 942 nsresult
michael@0 943 Database::InitTempTriggers()
michael@0 944 {
michael@0 945 MOZ_ASSERT(NS_IsMainThread());
michael@0 946
michael@0 947 nsresult rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERINSERT_TRIGGER);
michael@0 948 NS_ENSURE_SUCCESS(rv, rv);
michael@0 949 rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERDELETE_TRIGGER);
michael@0 950 NS_ENSURE_SUCCESS(rv, rv);
michael@0 951
michael@0 952 // Add the triggers that update the moz_hosts table as necessary.
michael@0 953 rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERINSERT_TRIGGER);
michael@0 954 NS_ENSURE_SUCCESS(rv, rv);
michael@0 955 rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERDELETE_TRIGGER);
michael@0 956 NS_ENSURE_SUCCESS(rv, rv);
michael@0 957 rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_FRECENCY_TRIGGER);
michael@0 958 NS_ENSURE_SUCCESS(rv, rv);
michael@0 959 rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_TYPED_TRIGGER);
michael@0 960 NS_ENSURE_SUCCESS(rv, rv);
michael@0 961
michael@0 962 return NS_OK;
michael@0 963 }
michael@0 964
michael@0 965 nsresult
michael@0 966 Database::UpdateBookmarkRootTitles()
michael@0 967 {
michael@0 968 MOZ_ASSERT(NS_IsMainThread());
michael@0 969
michael@0 970 nsCOMPtr<nsIStringBundleService> bundleService =
michael@0 971 services::GetStringBundleService();
michael@0 972 NS_ENSURE_STATE(bundleService);
michael@0 973
michael@0 974 nsCOMPtr<nsIStringBundle> bundle;
michael@0 975 nsresult rv = bundleService->CreateBundle(PLACES_BUNDLE, getter_AddRefs(bundle));
michael@0 976 if (NS_FAILED(rv)) return rv;
michael@0 977
michael@0 978 nsCOMPtr<mozIStorageAsyncStatement> stmt;
michael@0 979 rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 980 "UPDATE moz_bookmarks SET title = :new_title WHERE id = "
michael@0 981 "(SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = :root_name)"
michael@0 982 ), getter_AddRefs(stmt));
michael@0 983 if (NS_FAILED(rv)) return rv;
michael@0 984
michael@0 985 nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
michael@0 986 rv = stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
michael@0 987 if (NS_FAILED(rv)) return rv;
michael@0 988
michael@0 989 const char *rootNames[] = { "menu", "toolbar", "tags", "unfiled" };
michael@0 990 const char *titleStringIDs[] = {
michael@0 991 "BookmarksMenuFolderTitle", "BookmarksToolbarFolderTitle",
michael@0 992 "TagsFolderTitle", "UnsortedBookmarksFolderTitle"
michael@0 993 };
michael@0 994
michael@0 995 for (uint32_t i = 0; i < ArrayLength(rootNames); ++i) {
michael@0 996 nsXPIDLString title;
michael@0 997 rv = bundle->GetStringFromName(NS_ConvertASCIItoUTF16(titleStringIDs[i]).get(),
michael@0 998 getter_Copies(title));
michael@0 999 if (NS_FAILED(rv)) return rv;
michael@0 1000
michael@0 1001 nsCOMPtr<mozIStorageBindingParams> params;
michael@0 1002 rv = paramsArray->NewBindingParams(getter_AddRefs(params));
michael@0 1003 if (NS_FAILED(rv)) return rv;
michael@0 1004 rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("root_name"),
michael@0 1005 nsDependentCString(rootNames[i]));
michael@0 1006 if (NS_FAILED(rv)) return rv;
michael@0 1007 rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("new_title"),
michael@0 1008 NS_ConvertUTF16toUTF8(title));
michael@0 1009 if (NS_FAILED(rv)) return rv;
michael@0 1010 rv = paramsArray->AddParams(params);
michael@0 1011 if (NS_FAILED(rv)) return rv;
michael@0 1012 }
michael@0 1013
michael@0 1014 rv = stmt->BindParameters(paramsArray);
michael@0 1015 if (NS_FAILED(rv)) return rv;
michael@0 1016 nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
michael@0 1017 rv = stmt->ExecuteAsync(nullptr, getter_AddRefs(pendingStmt));
michael@0 1018 if (NS_FAILED(rv)) return rv;
michael@0 1019
michael@0 1020 return NS_OK;
michael@0 1021 }
michael@0 1022
michael@0 1023 nsresult
michael@0 1024 Database::CheckAndUpdateGUIDs()
michael@0 1025 {
michael@0 1026 MOZ_ASSERT(NS_IsMainThread());
michael@0 1027
michael@0 1028 // First, import any bookmark guids already set by Sync.
michael@0 1029 nsCOMPtr<mozIStorageStatement> updateStmt;
michael@0 1030 nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1031 "UPDATE moz_bookmarks "
michael@0 1032 "SET guid = :guid "
michael@0 1033 "WHERE id = :item_id "
michael@0 1034 ), getter_AddRefs(updateStmt));
michael@0 1035 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1036
michael@0 1037 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 1038 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1039 "SELECT item_id, content "
michael@0 1040 "FROM moz_items_annos "
michael@0 1041 "JOIN moz_anno_attributes "
michael@0 1042 "WHERE name = :anno_name "
michael@0 1043 ), getter_AddRefs(stmt));
michael@0 1044 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1045 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
michael@0 1046 SYNCGUID_ANNO);
michael@0 1047 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1048
michael@0 1049 bool hasResult;
michael@0 1050 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
michael@0 1051 int64_t itemId;
michael@0 1052 rv = stmt->GetInt64(0, &itemId);
michael@0 1053 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1054 nsAutoCString guid;
michael@0 1055 rv = stmt->GetUTF8String(1, guid);
michael@0 1056 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1057
michael@0 1058 // If we have an invalid guid, we don't need to do any more work.
michael@0 1059 if (!IsValidGUID(guid)) {
michael@0 1060 continue;
michael@0 1061 }
michael@0 1062
michael@0 1063 mozStorageStatementScoper updateScoper(updateStmt);
michael@0 1064 rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"), itemId);
michael@0 1065 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1066 rv = updateStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
michael@0 1067 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1068 rv = updateStmt->Execute();
michael@0 1069 if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
michael@0 1070 // We just tried to insert a duplicate guid. Ignore this error, and we
michael@0 1071 // will generate a new one next.
michael@0 1072 continue;
michael@0 1073 }
michael@0 1074 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1075 }
michael@0 1076
michael@0 1077 // Now, remove all the bookmark guid annotations that we just imported.
michael@0 1078 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1079 "DELETE FROM moz_items_annos "
michael@0 1080 "WHERE anno_attribute_id = ( "
michael@0 1081 "SELECT id "
michael@0 1082 "FROM moz_anno_attributes "
michael@0 1083 "WHERE name = :anno_name "
michael@0 1084 ") "
michael@0 1085 ), getter_AddRefs(stmt));
michael@0 1086 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1087 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
michael@0 1088 SYNCGUID_ANNO);
michael@0 1089 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1090
michael@0 1091 rv = stmt->Execute();
michael@0 1092 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1093
michael@0 1094 // Next, generate guids for any bookmark that does not already have one.
michael@0 1095 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1096 "UPDATE moz_bookmarks "
michael@0 1097 "SET guid = GENERATE_GUID() "
michael@0 1098 "WHERE guid IS NULL "
michael@0 1099 ), getter_AddRefs(stmt));
michael@0 1100 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1101
michael@0 1102 rv = stmt->Execute();
michael@0 1103 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1104
michael@0 1105 // Now, import any history guids already set by Sync.
michael@0 1106 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1107 "UPDATE moz_places "
michael@0 1108 "SET guid = :guid "
michael@0 1109 "WHERE id = :place_id "
michael@0 1110 ), getter_AddRefs(updateStmt));
michael@0 1111 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1112
michael@0 1113 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1114 "SELECT place_id, content "
michael@0 1115 "FROM moz_annos "
michael@0 1116 "JOIN moz_anno_attributes "
michael@0 1117 "WHERE name = :anno_name "
michael@0 1118 ), getter_AddRefs(stmt));
michael@0 1119 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1120 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
michael@0 1121 SYNCGUID_ANNO);
michael@0 1122 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1123
michael@0 1124 while (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
michael@0 1125 int64_t placeId;
michael@0 1126 rv = stmt->GetInt64(0, &placeId);
michael@0 1127 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1128 nsAutoCString guid;
michael@0 1129 rv = stmt->GetUTF8String(1, guid);
michael@0 1130 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1131
michael@0 1132 // If we have an invalid guid, we don't need to do any more work.
michael@0 1133 if (!IsValidGUID(guid)) {
michael@0 1134 continue;
michael@0 1135 }
michael@0 1136
michael@0 1137 mozStorageStatementScoper updateScoper(updateStmt);
michael@0 1138 rv = updateStmt->BindInt64ByName(NS_LITERAL_CSTRING("place_id"), placeId);
michael@0 1139 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1140 rv = updateStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), guid);
michael@0 1141 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1142 rv = updateStmt->Execute();
michael@0 1143 if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
michael@0 1144 // We just tried to insert a duplicate guid. Ignore this error, and we
michael@0 1145 // will generate a new one next.
michael@0 1146 continue;
michael@0 1147 }
michael@0 1148 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1149 }
michael@0 1150
michael@0 1151 // Now, remove all the place guid annotations that we just imported.
michael@0 1152 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1153 "DELETE FROM moz_annos "
michael@0 1154 "WHERE anno_attribute_id = ( "
michael@0 1155 "SELECT id "
michael@0 1156 "FROM moz_anno_attributes "
michael@0 1157 "WHERE name = :anno_name "
michael@0 1158 ") "
michael@0 1159 ), getter_AddRefs(stmt));
michael@0 1160 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1161 rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"),
michael@0 1162 SYNCGUID_ANNO);
michael@0 1163 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1164
michael@0 1165 rv = stmt->Execute();
michael@0 1166 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1167
michael@0 1168 // Finally, generate guids for any places that do not already have one.
michael@0 1169 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1170 "UPDATE moz_places "
michael@0 1171 "SET guid = GENERATE_GUID() "
michael@0 1172 "WHERE guid IS NULL "
michael@0 1173 ), getter_AddRefs(stmt));
michael@0 1174 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1175
michael@0 1176 rv = stmt->Execute();
michael@0 1177 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1178
michael@0 1179 return NS_OK;
michael@0 1180 }
michael@0 1181
michael@0 1182 nsresult
michael@0 1183 Database::MigrateV7Up()
michael@0 1184 {
michael@0 1185 MOZ_ASSERT(NS_IsMainThread());
michael@0 1186
michael@0 1187 // Some old v6 databases come from alpha versions that missed indices.
michael@0 1188 // Just bail out and replace the database in such a case.
michael@0 1189 bool URLUniqueIndexExists = false;
michael@0 1190 nsresult rv = mMainConn->IndexExists(NS_LITERAL_CSTRING(
michael@0 1191 "moz_places_url_uniqueindex"
michael@0 1192 ), &URLUniqueIndexExists);
michael@0 1193 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1194 if (!URLUniqueIndexExists) {
michael@0 1195 return NS_ERROR_FILE_CORRUPTED;
michael@0 1196 }
michael@0 1197
michael@0 1198 mozStorageTransaction transaction(mMainConn, false);
michael@0 1199
michael@0 1200 // We need an index on lastModified to catch quickly last modified bookmark
michael@0 1201 // title for tag container's children. This will be useful for Sync, too.
michael@0 1202 bool lastModIndexExists = false;
michael@0 1203 rv = mMainConn->IndexExists(
michael@0 1204 NS_LITERAL_CSTRING("moz_bookmarks_itemlastmodifiedindex"),
michael@0 1205 &lastModIndexExists);
michael@0 1206 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1207
michael@0 1208 if (!lastModIndexExists) {
michael@0 1209 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
michael@0 1210 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1211 }
michael@0 1212
michael@0 1213 // We need to do a one-time change of the moz_historyvisits.pageindex
michael@0 1214 // to speed up finding last visit date when joinin with moz_places.
michael@0 1215 // See bug 392399 for more details.
michael@0 1216 bool pageIndexExists = false;
michael@0 1217 rv = mMainConn->IndexExists(
michael@0 1218 NS_LITERAL_CSTRING("moz_historyvisits_pageindex"), &pageIndexExists);
michael@0 1219 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1220
michael@0 1221 if (pageIndexExists) {
michael@0 1222 // drop old index
michael@0 1223 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1224 "DROP INDEX IF EXISTS moz_historyvisits_pageindex"));
michael@0 1225 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1226
michael@0 1227 // create the new multi-column index
michael@0 1228 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_PLACEDATE);
michael@0 1229 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1230 }
michael@0 1231
michael@0 1232 // for existing profiles, we may not have a frecency column
michael@0 1233 nsCOMPtr<mozIStorageStatement> hasFrecencyStatement;
michael@0 1234 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1235 "SELECT frecency FROM moz_places"),
michael@0 1236 getter_AddRefs(hasFrecencyStatement));
michael@0 1237
michael@0 1238 if (NS_FAILED(rv)) {
michael@0 1239 // Add frecency column to moz_places, default to -1 so that all the
michael@0 1240 // frecencies are invalid
michael@0 1241 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1242 "ALTER TABLE moz_places ADD frecency INTEGER DEFAULT -1 NOT NULL"));
michael@0 1243 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1244
michael@0 1245 // create index for the frecency column
michael@0 1246 // XXX multi column index with typed, and visit_count?
michael@0 1247 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FRECENCY);
michael@0 1248 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1249
michael@0 1250 // Invalidate all frecencies, since they need recalculation.
michael@0 1251 nsCOMPtr<mozIStorageAsyncStatement> stmt = GetAsyncStatement(
michael@0 1252 "UPDATE moz_places SET frecency = ( "
michael@0 1253 "CASE "
michael@0 1254 "WHEN url BETWEEN 'place:' AND 'place;' "
michael@0 1255 "THEN 0 "
michael@0 1256 "ELSE -1 "
michael@0 1257 "END "
michael@0 1258 ") "
michael@0 1259 );
michael@0 1260 NS_ENSURE_STATE(stmt);
michael@0 1261 nsCOMPtr<mozIStoragePendingStatement> ps;
michael@0 1262 (void)stmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
michael@0 1263 }
michael@0 1264
michael@0 1265 // Temporary migration code for bug 396300
michael@0 1266 nsCOMPtr<mozIStorageStatement> moveUnfiledBookmarks;
michael@0 1267 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1268 "UPDATE moz_bookmarks "
michael@0 1269 "SET parent = ("
michael@0 1270 "SELECT folder_id "
michael@0 1271 "FROM moz_bookmarks_roots "
michael@0 1272 "WHERE root_name = :root_name "
michael@0 1273 ") "
michael@0 1274 "WHERE type = :item_type "
michael@0 1275 "AND parent = ("
michael@0 1276 "SELECT folder_id "
michael@0 1277 "FROM moz_bookmarks_roots "
michael@0 1278 "WHERE root_name = :parent_name "
michael@0 1279 ")"),
michael@0 1280 getter_AddRefs(moveUnfiledBookmarks));
michael@0 1281 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1282 rv = moveUnfiledBookmarks->BindUTF8StringByName(
michael@0 1283 NS_LITERAL_CSTRING("root_name"), NS_LITERAL_CSTRING("unfiled")
michael@0 1284 );
michael@0 1285 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1286 rv = moveUnfiledBookmarks->BindInt32ByName(
michael@0 1287 NS_LITERAL_CSTRING("item_type"), nsINavBookmarksService::TYPE_BOOKMARK
michael@0 1288 );
michael@0 1289 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1290 rv = moveUnfiledBookmarks->BindUTF8StringByName(
michael@0 1291 NS_LITERAL_CSTRING("parent_name"), NS_LITERAL_CSTRING("places")
michael@0 1292 );
michael@0 1293 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1294 rv = moveUnfiledBookmarks->Execute();
michael@0 1295 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1296
michael@0 1297 // Create a statement to test for trigger creation
michael@0 1298 nsCOMPtr<mozIStorageStatement> triggerDetection;
michael@0 1299 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1300 "SELECT name "
michael@0 1301 "FROM sqlite_master "
michael@0 1302 "WHERE type = 'trigger' "
michael@0 1303 "AND name = :trigger_name"),
michael@0 1304 getter_AddRefs(triggerDetection));
michael@0 1305 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1306
michael@0 1307 // Check for existence
michael@0 1308 bool triggerExists;
michael@0 1309 rv = triggerDetection->BindUTF8StringByName(
michael@0 1310 NS_LITERAL_CSTRING("trigger_name"),
michael@0 1311 NS_LITERAL_CSTRING("moz_historyvisits_afterinsert_v1_trigger")
michael@0 1312 );
michael@0 1313 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1314 rv = triggerDetection->ExecuteStep(&triggerExists);
michael@0 1315 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1316 rv = triggerDetection->Reset();
michael@0 1317 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1318
michael@0 1319 // We need to create two triggers on moz_historyvists to maintain the
michael@0 1320 // accuracy of moz_places.visit_count. For this to work, we must ensure that
michael@0 1321 // all moz_places.visit_count values are correct.
michael@0 1322 // See bug 416313 for details.
michael@0 1323 if (!triggerExists) {
michael@0 1324 // First, we do a one-time reset of all the moz_places.visit_count values.
michael@0 1325 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1326 "UPDATE moz_places SET visit_count = "
michael@0 1327 "(SELECT count(*) FROM moz_historyvisits "
michael@0 1328 "WHERE place_id = moz_places.id "
michael@0 1329 "AND visit_type NOT IN ") +
michael@0 1330 nsPrintfCString("(0,%d,%d,%d) ",
michael@0 1331 nsINavHistoryService::TRANSITION_EMBED,
michael@0 1332 nsINavHistoryService::TRANSITION_FRAMED_LINK,
michael@0 1333 nsINavHistoryService::TRANSITION_DOWNLOAD) +
michael@0 1334 NS_LITERAL_CSTRING(")"));
michael@0 1335 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1336
michael@0 1337 // We used to create two triggers here, but we no longer need that with
michael@0 1338 // schema version eight and greater. We've removed their creation here as
michael@0 1339 // a result.
michael@0 1340 }
michael@0 1341
michael@0 1342 // Check for existence
michael@0 1343 rv = triggerDetection->BindUTF8StringByName(
michael@0 1344 NS_LITERAL_CSTRING("trigger_name"),
michael@0 1345 NS_LITERAL_CSTRING("moz_bookmarks_beforedelete_v1_trigger")
michael@0 1346 );
michael@0 1347 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1348 rv = triggerDetection->ExecuteStep(&triggerExists);
michael@0 1349 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1350 rv = triggerDetection->Reset();
michael@0 1351 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1352
michael@0 1353 // We need to create one trigger on moz_bookmarks to remove unused keywords.
michael@0 1354 // See bug 421180 for details.
michael@0 1355 if (!triggerExists) {
michael@0 1356 // First, remove any existing dangling keywords
michael@0 1357 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1358 "DELETE FROM moz_keywords "
michael@0 1359 "WHERE id IN ("
michael@0 1360 "SELECT k.id "
michael@0 1361 "FROM moz_keywords k "
michael@0 1362 "LEFT OUTER JOIN moz_bookmarks b "
michael@0 1363 "ON b.keyword_id = k.id "
michael@0 1364 "WHERE b.id IS NULL"
michael@0 1365 ")"));
michael@0 1366 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1367 }
michael@0 1368
michael@0 1369 // Add the moz_inputhistory table, if missing.
michael@0 1370 bool tableExists = false;
michael@0 1371 rv = mMainConn->TableExists(NS_LITERAL_CSTRING("moz_inputhistory"),
michael@0 1372 &tableExists);
michael@0 1373 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1374 if (!tableExists) {
michael@0 1375 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_INPUTHISTORY);
michael@0 1376 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1377 }
michael@0 1378
michael@0 1379 return transaction.Commit();
michael@0 1380 }
michael@0 1381
michael@0 1382
michael@0 1383 nsresult
michael@0 1384 Database::MigrateV8Up()
michael@0 1385 {
michael@0 1386 MOZ_ASSERT(NS_IsMainThread());
michael@0 1387 mozStorageTransaction transaction(mMainConn, false);
michael@0 1388
michael@0 1389 nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1390 "DROP TRIGGER IF EXISTS moz_historyvisits_afterinsert_v1_trigger"));
michael@0 1391 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1392
michael@0 1393 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1394 "DROP TRIGGER IF EXISTS moz_historyvisits_afterdelete_v1_trigger"));
michael@0 1395 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1396
michael@0 1397
michael@0 1398 // bug #381795 - remove unused indexes
michael@0 1399 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1400 "DROP INDEX IF EXISTS moz_places_titleindex"));
michael@0 1401 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1402 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1403 "DROP INDEX IF EXISTS moz_annos_item_idindex"));
michael@0 1404 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1405 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1406 "DROP INDEX IF EXISTS moz_annos_place_idindex"));
michael@0 1407 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1408
michael@0 1409 // Do a one-time re-creation of the moz_annos indexes (bug 415201)
michael@0 1410 bool oldIndexExists = false;
michael@0 1411 rv = mMainConn->IndexExists(NS_LITERAL_CSTRING("moz_annos_attributesindex"), &oldIndexExists);
michael@0 1412 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1413 if (oldIndexExists) {
michael@0 1414 // drop old uri annos index
michael@0 1415 rv = mMainConn->ExecuteSimpleSQL(
michael@0 1416 NS_LITERAL_CSTRING("DROP INDEX moz_annos_attributesindex"));
michael@0 1417 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1418
michael@0 1419 // create new uri annos index
michael@0 1420 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ANNOS_PLACEATTRIBUTE);
michael@0 1421 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1422
michael@0 1423 // drop old item annos index
michael@0 1424 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1425 "DROP INDEX IF EXISTS moz_items_annos_attributesindex"));
michael@0 1426 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1427
michael@0 1428 // create new item annos index
michael@0 1429 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
michael@0 1430 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1431 }
michael@0 1432
michael@0 1433 return transaction.Commit();
michael@0 1434 }
michael@0 1435
michael@0 1436
michael@0 1437 nsresult
michael@0 1438 Database::MigrateV9Up()
michael@0 1439 {
michael@0 1440 MOZ_ASSERT(NS_IsMainThread());
michael@0 1441 mozStorageTransaction transaction(mMainConn, false);
michael@0 1442 // Added in Bug 488966. The last_visit_date column caches the last
michael@0 1443 // visit date, this enhances SELECT performances when we
michael@0 1444 // need to sort visits by visit date.
michael@0 1445 // The cached value is synced by triggers on every added or removed visit.
michael@0 1446 // See nsPlacesTriggers.h for details on the triggers.
michael@0 1447 bool oldIndexExists = false;
michael@0 1448 nsresult rv = mMainConn->IndexExists(
michael@0 1449 NS_LITERAL_CSTRING("moz_places_lastvisitdateindex"), &oldIndexExists);
michael@0 1450 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1451
michael@0 1452 if (!oldIndexExists) {
michael@0 1453 // Add last_visit_date column to moz_places.
michael@0 1454 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1455 "ALTER TABLE moz_places ADD last_visit_date INTEGER"));
michael@0 1456 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1457
michael@0 1458 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_LASTVISITDATE);
michael@0 1459 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1460
michael@0 1461 // Now let's sync the column contents with real visit dates.
michael@0 1462 // This query can be really slow due to disk access, since it will basically
michael@0 1463 // dupe the table contents in the journal file, and then write them down
michael@0 1464 // in the database.
michael@0 1465 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1466 "UPDATE moz_places SET last_visit_date = "
michael@0 1467 "(SELECT MAX(visit_date) "
michael@0 1468 "FROM moz_historyvisits "
michael@0 1469 "WHERE place_id = moz_places.id)"));
michael@0 1470 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1471 }
michael@0 1472
michael@0 1473 return transaction.Commit();
michael@0 1474 }
michael@0 1475
michael@0 1476
michael@0 1477 nsresult
michael@0 1478 Database::MigrateV10Up()
michael@0 1479 {
michael@0 1480 MOZ_ASSERT(NS_IsMainThread());
michael@0 1481 // LastModified is set to the same value as dateAdded on item creation.
michael@0 1482 // This way we can use lastModified index to sort.
michael@0 1483 nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1484 "UPDATE moz_bookmarks SET lastModified = dateAdded "
michael@0 1485 "WHERE lastModified IS NULL"));
michael@0 1486 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1487
michael@0 1488 return NS_OK;
michael@0 1489 }
michael@0 1490
michael@0 1491
michael@0 1492 nsresult
michael@0 1493 Database::MigrateV11Up()
michael@0 1494 {
michael@0 1495 MOZ_ASSERT(NS_IsMainThread());
michael@0 1496 // Temp tables are going away.
michael@0 1497 // For triggers correctness, every time we pass through this migration
michael@0 1498 // step, we must ensure correctness of visit_count values.
michael@0 1499 nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1500 "UPDATE moz_places SET visit_count = "
michael@0 1501 "(SELECT count(*) FROM moz_historyvisits "
michael@0 1502 "WHERE place_id = moz_places.id "
michael@0 1503 "AND visit_type NOT IN ") +
michael@0 1504 nsPrintfCString("(0,%d,%d,%d) ",
michael@0 1505 nsINavHistoryService::TRANSITION_EMBED,
michael@0 1506 nsINavHistoryService::TRANSITION_FRAMED_LINK,
michael@0 1507 nsINavHistoryService::TRANSITION_DOWNLOAD) +
michael@0 1508 NS_LITERAL_CSTRING(")")
michael@0 1509 );
michael@0 1510 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1511
michael@0 1512 // For existing profiles, we may not have a moz_bookmarks.guid column
michael@0 1513 nsCOMPtr<mozIStorageStatement> hasGuidStatement;
michael@0 1514 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1515 "SELECT guid FROM moz_bookmarks"),
michael@0 1516 getter_AddRefs(hasGuidStatement));
michael@0 1517
michael@0 1518 if (NS_FAILED(rv)) {
michael@0 1519 // moz_bookmarks grew a guid column. Add the column, but do not populate it
michael@0 1520 // with anything just yet. We will do that soon.
michael@0 1521 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1522 "ALTER TABLE moz_bookmarks "
michael@0 1523 "ADD COLUMN guid TEXT"
michael@0 1524 ));
michael@0 1525 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1526
michael@0 1527 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
michael@0 1528 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1529
michael@0 1530 // moz_places grew a guid column. Add the column, but do not populate it
michael@0 1531 // with anything just yet. We will do that soon.
michael@0 1532 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1533 "ALTER TABLE moz_places "
michael@0 1534 "ADD COLUMN guid TEXT"
michael@0 1535 ));
michael@0 1536 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1537
michael@0 1538 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
michael@0 1539 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1540 }
michael@0 1541
michael@0 1542 // We need to update our guids before we do any real database work.
michael@0 1543 rv = CheckAndUpdateGUIDs();
michael@0 1544 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1545
michael@0 1546 return NS_OK;
michael@0 1547 }
michael@0 1548
michael@0 1549 nsresult
michael@0 1550 Database::MigrateV13Up()
michael@0 1551 {
michael@0 1552 MOZ_ASSERT(NS_IsMainThread());
michael@0 1553
michael@0 1554 // Dynamic containers are no longer supported.
michael@0 1555 nsCOMPtr<mozIStorageAsyncStatement> deleteDynContainersStmt;
michael@0 1556 nsresult rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 1557 "DELETE FROM moz_bookmarks WHERE type = :item_type"),
michael@0 1558 getter_AddRefs(deleteDynContainersStmt));
michael@0 1559 rv = deleteDynContainersStmt->BindInt32ByName(
michael@0 1560 NS_LITERAL_CSTRING("item_type"),
michael@0 1561 nsINavBookmarksService::TYPE_DYNAMIC_CONTAINER
michael@0 1562 );
michael@0 1563 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1564 nsCOMPtr<mozIStoragePendingStatement> ps;
michael@0 1565 rv = deleteDynContainersStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
michael@0 1566 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1567
michael@0 1568 return NS_OK;
michael@0 1569 }
michael@0 1570
michael@0 1571 nsresult
michael@0 1572 Database::MigrateV14Up()
michael@0 1573 {
michael@0 1574 MOZ_ASSERT(NS_IsMainThread());
michael@0 1575
michael@0 1576 // For existing profiles, we may not have a moz_favicons.guid column.
michael@0 1577 // Add it here. We want it to be unique, but ALTER TABLE doesn't allow
michael@0 1578 // a uniqueness constraint, so the index must be created separately.
michael@0 1579 nsCOMPtr<mozIStorageStatement> hasGuidStatement;
michael@0 1580 nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1581 "SELECT guid FROM moz_favicons"),
michael@0 1582 getter_AddRefs(hasGuidStatement));
michael@0 1583
michael@0 1584 if (NS_FAILED(rv)) {
michael@0 1585 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1586 "ALTER TABLE moz_favicons "
michael@0 1587 "ADD COLUMN guid TEXT"
michael@0 1588 ));
michael@0 1589 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1590
michael@0 1591 rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_FAVICONS_GUID);
michael@0 1592 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1593 }
michael@0 1594
michael@0 1595 // Generate GUID for any favicon missing it.
michael@0 1596 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1597 "UPDATE moz_favicons "
michael@0 1598 "SET guid = GENERATE_GUID() "
michael@0 1599 "WHERE guid ISNULL "
michael@0 1600 ));
michael@0 1601 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1602
michael@0 1603 return NS_OK;
michael@0 1604 }
michael@0 1605
michael@0 1606 nsresult
michael@0 1607 Database::MigrateV15Up()
michael@0 1608 {
michael@0 1609 MOZ_ASSERT(NS_IsMainThread());
michael@0 1610
michael@0 1611 // Drop moz_bookmarks_beforedelete_v1_trigger, since it's more expensive than
michael@0 1612 // useful.
michael@0 1613 nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1614 "DROP TRIGGER IF EXISTS moz_bookmarks_beforedelete_v1_trigger"
michael@0 1615 ));
michael@0 1616 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1617
michael@0 1618 // Remove any orphan keywords.
michael@0 1619 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1620 "DELETE FROM moz_keywords "
michael@0 1621 "WHERE NOT EXISTS ( "
michael@0 1622 "SELECT id "
michael@0 1623 "FROM moz_bookmarks "
michael@0 1624 "WHERE keyword_id = moz_keywords.id "
michael@0 1625 ")"
michael@0 1626 ));
michael@0 1627 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1628
michael@0 1629 return NS_OK;
michael@0 1630 }
michael@0 1631
michael@0 1632 nsresult
michael@0 1633 Database::MigrateV16Up()
michael@0 1634 {
michael@0 1635 MOZ_ASSERT(NS_IsMainThread());
michael@0 1636
michael@0 1637 // Due to Bug 715268 downgraded and then upgraded profiles may lack favicons
michael@0 1638 // guids, so fillup any missing ones.
michael@0 1639 nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1640 "UPDATE moz_favicons "
michael@0 1641 "SET guid = GENERATE_GUID() "
michael@0 1642 "WHERE guid ISNULL "
michael@0 1643 ));
michael@0 1644 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1645
michael@0 1646 return NS_OK;
michael@0 1647 }
michael@0 1648
michael@0 1649 nsresult
michael@0 1650 Database::MigrateV17Up()
michael@0 1651 {
michael@0 1652 MOZ_ASSERT(NS_IsMainThread());
michael@0 1653
michael@0 1654 bool tableExists = false;
michael@0 1655
michael@0 1656 nsresult rv = mMainConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
michael@0 1657 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1658
michael@0 1659 if (!tableExists) {
michael@0 1660 // For anyone who used in-development versions of this autocomplete,
michael@0 1661 // drop the old tables and its indexes.
michael@0 1662 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1663 "DROP INDEX IF EXISTS moz_hostnames_frecencyindex"
michael@0 1664 ));
michael@0 1665 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1666 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1667 "DROP TABLE IF EXISTS moz_hostnames"
michael@0 1668 ));
michael@0 1669 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1670
michael@0 1671 // Add the moz_hosts table so we can get hostnames for URL autocomplete.
michael@0 1672 rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS);
michael@0 1673 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1674 }
michael@0 1675
michael@0 1676 // Fill the moz_hosts table with all the domains in moz_places.
michael@0 1677 nsCOMPtr<mozIStorageAsyncStatement> fillHostsStmt;
michael@0 1678 rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 1679 "INSERT OR IGNORE INTO moz_hosts (host, frecency) "
michael@0 1680 "SELECT fixup_url(get_unreversed_host(h.rev_host)) AS host, "
michael@0 1681 "(SELECT MAX(frecency) FROM moz_places "
michael@0 1682 "WHERE rev_host = h.rev_host "
michael@0 1683 "OR rev_host = h.rev_host || 'www.' "
michael@0 1684 ") AS frecency "
michael@0 1685 "FROM moz_places h "
michael@0 1686 "WHERE LENGTH(h.rev_host) > 1 "
michael@0 1687 "GROUP BY h.rev_host"
michael@0 1688 ), getter_AddRefs(fillHostsStmt));
michael@0 1689 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1690
michael@0 1691 nsCOMPtr<mozIStoragePendingStatement> ps;
michael@0 1692 rv = fillHostsStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
michael@0 1693 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1694
michael@0 1695 return NS_OK;
michael@0 1696 }
michael@0 1697
michael@0 1698 nsresult
michael@0 1699 Database::MigrateV18Up()
michael@0 1700 {
michael@0 1701 MOZ_ASSERT(NS_IsMainThread());
michael@0 1702
michael@0 1703 // moz_hosts should distinguish on typed entries.
michael@0 1704
michael@0 1705 // Check if the profile already has a typed column.
michael@0 1706 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 1707 nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1708 "SELECT typed FROM moz_hosts"
michael@0 1709 ), getter_AddRefs(stmt));
michael@0 1710 if (NS_FAILED(rv)) {
michael@0 1711 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1712 "ALTER TABLE moz_hosts ADD COLUMN typed NOT NULL DEFAULT 0"
michael@0 1713 ));
michael@0 1714 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1715 }
michael@0 1716
michael@0 1717 // With the addition of the typed column the covering index loses its
michael@0 1718 // advantages. On the other side querying on host and (optionally) typed
michael@0 1719 // largely restricts the number of results, making scans decently fast.
michael@0 1720 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1721 "DROP INDEX IF EXISTS moz_hosts_frecencyhostindex"
michael@0 1722 ));
michael@0 1723 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1724
michael@0 1725 // Update typed data.
michael@0 1726 nsCOMPtr<mozIStorageAsyncStatement> updateTypedStmt;
michael@0 1727 rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 1728 "UPDATE moz_hosts SET typed = 1 WHERE host IN ( "
michael@0 1729 "SELECT fixup_url(get_unreversed_host(rev_host)) "
michael@0 1730 "FROM moz_places WHERE typed = 1 "
michael@0 1731 ") "
michael@0 1732 ), getter_AddRefs(updateTypedStmt));
michael@0 1733 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1734
michael@0 1735 nsCOMPtr<mozIStoragePendingStatement> ps;
michael@0 1736 rv = updateTypedStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
michael@0 1737 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1738
michael@0 1739 return NS_OK;
michael@0 1740 }
michael@0 1741
michael@0 1742 nsresult
michael@0 1743 Database::MigrateV19Up()
michael@0 1744 {
michael@0 1745 MOZ_ASSERT(NS_IsMainThread());
michael@0 1746
michael@0 1747 // Livemarks children are no longer bookmarks.
michael@0 1748
michael@0 1749 // Remove all children of folders annotated as livemarks.
michael@0 1750 nsCOMPtr<mozIStorageStatement> deleteLivemarksChildrenStmt;
michael@0 1751 nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1752 "DELETE FROM moz_bookmarks WHERE parent IN("
michael@0 1753 "SELECT b.id FROM moz_bookmarks b "
michael@0 1754 "JOIN moz_items_annos a ON a.item_id = b.id "
michael@0 1755 "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
michael@0 1756 "WHERE b.type = :item_type AND n.name = :anno_name "
michael@0 1757 ")"
michael@0 1758 ), getter_AddRefs(deleteLivemarksChildrenStmt));
michael@0 1759 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1760 rv = deleteLivemarksChildrenStmt->BindUTF8StringByName(
michael@0 1761 NS_LITERAL_CSTRING("anno_name"), NS_LITERAL_CSTRING(LMANNO_FEEDURI)
michael@0 1762 );
michael@0 1763 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1764 rv = deleteLivemarksChildrenStmt->BindInt32ByName(
michael@0 1765 NS_LITERAL_CSTRING("item_type"), nsINavBookmarksService::TYPE_FOLDER
michael@0 1766 );
michael@0 1767 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1768 rv = deleteLivemarksChildrenStmt->Execute();
michael@0 1769 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1770
michael@0 1771 // Clear obsolete livemark prefs.
michael@0 1772 (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_seconds");
michael@0 1773 (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_limit_count");
michael@0 1774 (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_delay_time");
michael@0 1775
michael@0 1776 // Remove the old status annotations.
michael@0 1777 nsCOMPtr<mozIStorageStatement> deleteLivemarksAnnosStmt;
michael@0 1778 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1779 "DELETE FROM moz_items_annos WHERE anno_attribute_id IN("
michael@0 1780 "SELECT id FROM moz_anno_attributes "
michael@0 1781 "WHERE name IN (:anno_loading, :anno_loadfailed, :anno_expiration) "
michael@0 1782 ")"
michael@0 1783 ), getter_AddRefs(deleteLivemarksAnnosStmt));
michael@0 1784 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1785 rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
michael@0 1786 NS_LITERAL_CSTRING("anno_loading"), NS_LITERAL_CSTRING("livemark/loading")
michael@0 1787 );
michael@0 1788 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1789 rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
michael@0 1790 NS_LITERAL_CSTRING("anno_loadfailed"), NS_LITERAL_CSTRING("livemark/loadfailed")
michael@0 1791 );
michael@0 1792 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1793 rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
michael@0 1794 NS_LITERAL_CSTRING("anno_expiration"), NS_LITERAL_CSTRING("livemark/expiration")
michael@0 1795 );
michael@0 1796 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1797 rv = deleteLivemarksAnnosStmt->Execute();
michael@0 1798 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1799
michael@0 1800 // Remove orphan annotation names.
michael@0 1801 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1802 "DELETE FROM moz_anno_attributes "
michael@0 1803 "WHERE name IN (:anno_loading, :anno_loadfailed, :anno_expiration) "
michael@0 1804 ), getter_AddRefs(deleteLivemarksAnnosStmt));
michael@0 1805 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1806 rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
michael@0 1807 NS_LITERAL_CSTRING("anno_loading"), NS_LITERAL_CSTRING("livemark/loading")
michael@0 1808 );
michael@0 1809 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1810 rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
michael@0 1811 NS_LITERAL_CSTRING("anno_loadfailed"), NS_LITERAL_CSTRING("livemark/loadfailed")
michael@0 1812 );
michael@0 1813 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1814 rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
michael@0 1815 NS_LITERAL_CSTRING("anno_expiration"), NS_LITERAL_CSTRING("livemark/expiration")
michael@0 1816 );
michael@0 1817 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1818 rv = deleteLivemarksAnnosStmt->Execute();
michael@0 1819 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1820
michael@0 1821 return NS_OK;
michael@0 1822 }
michael@0 1823
michael@0 1824 nsresult
michael@0 1825 Database::MigrateV20Up()
michael@0 1826 {
michael@0 1827 MOZ_ASSERT(NS_IsMainThread());
michael@0 1828
michael@0 1829 // Remove obsolete bookmark GUID annotations.
michael@0 1830 nsCOMPtr<mozIStorageStatement> deleteOldBookmarkGUIDAnnosStmt;
michael@0 1831 nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1832 "DELETE FROM moz_items_annos WHERE anno_attribute_id = ("
michael@0 1833 "SELECT id FROM moz_anno_attributes "
michael@0 1834 "WHERE name = :anno_guid"
michael@0 1835 ")"
michael@0 1836 ), getter_AddRefs(deleteOldBookmarkGUIDAnnosStmt));
michael@0 1837 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1838 rv = deleteOldBookmarkGUIDAnnosStmt->BindUTF8StringByName(
michael@0 1839 NS_LITERAL_CSTRING("anno_guid"), NS_LITERAL_CSTRING("placesInternal/GUID")
michael@0 1840 );
michael@0 1841 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1842 rv = deleteOldBookmarkGUIDAnnosStmt->Execute();
michael@0 1843 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1844
michael@0 1845 // Remove the orphan annotation name.
michael@0 1846 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1847 "DELETE FROM moz_anno_attributes "
michael@0 1848 "WHERE name = :anno_guid"
michael@0 1849 ), getter_AddRefs(deleteOldBookmarkGUIDAnnosStmt));
michael@0 1850 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1851 rv = deleteOldBookmarkGUIDAnnosStmt->BindUTF8StringByName(
michael@0 1852 NS_LITERAL_CSTRING("anno_guid"), NS_LITERAL_CSTRING("placesInternal/GUID")
michael@0 1853 );
michael@0 1854 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1855 rv = deleteOldBookmarkGUIDAnnosStmt->Execute();
michael@0 1856 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1857
michael@0 1858 return NS_OK;
michael@0 1859 }
michael@0 1860
michael@0 1861 nsresult
michael@0 1862 Database::MigrateV21Up()
michael@0 1863 {
michael@0 1864 MOZ_ASSERT(NS_IsMainThread());
michael@0 1865
michael@0 1866 // Add a prefix column to moz_hosts.
michael@0 1867 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 1868 nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 1869 "SELECT prefix FROM moz_hosts"
michael@0 1870 ), getter_AddRefs(stmt));
michael@0 1871 if (NS_FAILED(rv)) {
michael@0 1872 rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1873 "ALTER TABLE moz_hosts ADD COLUMN prefix"
michael@0 1874 ));
michael@0 1875 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1876 }
michael@0 1877
michael@0 1878 return NS_OK;
michael@0 1879 }
michael@0 1880
michael@0 1881 nsresult
michael@0 1882 Database::MigrateV22Up()
michael@0 1883 {
michael@0 1884 MOZ_ASSERT(NS_IsMainThread());
michael@0 1885
michael@0 1886 // Reset all session IDs to 0 since we don't support them anymore.
michael@0 1887 // We don't set them to NULL to avoid breaking downgrades.
michael@0 1888 nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
michael@0 1889 "UPDATE moz_historyvisits SET session = 0"
michael@0 1890 ));
michael@0 1891 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1892
michael@0 1893 return NS_OK;
michael@0 1894 }
michael@0 1895
michael@0 1896
michael@0 1897 nsresult
michael@0 1898 Database::MigrateV23Up()
michael@0 1899 {
michael@0 1900 MOZ_ASSERT(NS_IsMainThread());
michael@0 1901
michael@0 1902 // Recalculate hosts prefixes.
michael@0 1903 nsCOMPtr<mozIStorageAsyncStatement> updatePrefixesStmt;
michael@0 1904 nsresult rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
michael@0 1905 "UPDATE moz_hosts SET prefix = ( " HOSTS_PREFIX_PRIORITY_FRAGMENT ") "
michael@0 1906 ), getter_AddRefs(updatePrefixesStmt));
michael@0 1907 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1908
michael@0 1909 nsCOMPtr<mozIStoragePendingStatement> ps;
michael@0 1910 rv = updatePrefixesStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
michael@0 1911 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1912
michael@0 1913 return NS_OK;
michael@0 1914 }
michael@0 1915
michael@0 1916 void
michael@0 1917 Database::Shutdown()
michael@0 1918 {
michael@0 1919 MOZ_ASSERT(NS_IsMainThread());
michael@0 1920 MOZ_ASSERT(!mShuttingDown);
michael@0 1921 MOZ_ASSERT(!mClosed);
michael@0 1922
michael@0 1923 mShuttingDown = true;
michael@0 1924
michael@0 1925 mMainThreadStatements.FinalizeStatements();
michael@0 1926 mMainThreadAsyncStatements.FinalizeStatements();
michael@0 1927
michael@0 1928 nsRefPtr< FinalizeStatementCacheProxy<mozIStorageStatement> > event =
michael@0 1929 new FinalizeStatementCacheProxy<mozIStorageStatement>(
michael@0 1930 mAsyncThreadStatements, NS_ISUPPORTS_CAST(nsIObserver*, this)
michael@0 1931 );
michael@0 1932 DispatchToAsyncThread(event);
michael@0 1933
michael@0 1934 mClosed = true;
michael@0 1935
michael@0 1936 nsRefPtr<ConnectionCloseCallback> closeListener =
michael@0 1937 new ConnectionCloseCallback();
michael@0 1938 (void)mMainConn->AsyncClose(closeListener);
michael@0 1939 }
michael@0 1940
michael@0 1941 ////////////////////////////////////////////////////////////////////////////////
michael@0 1942 //// nsIObserver
michael@0 1943
michael@0 1944 NS_IMETHODIMP
michael@0 1945 Database::Observe(nsISupports *aSubject,
michael@0 1946 const char *aTopic,
michael@0 1947 const char16_t *aData)
michael@0 1948 {
michael@0 1949 MOZ_ASSERT(NS_IsMainThread());
michael@0 1950
michael@0 1951 if (strcmp(aTopic, TOPIC_PROFILE_CHANGE_TEARDOWN) == 0) {
michael@0 1952 // Tests simulating shutdown may cause multiple notifications.
michael@0 1953 if (mShuttingDown) {
michael@0 1954 return NS_OK;
michael@0 1955 }
michael@0 1956
michael@0 1957 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
michael@0 1958 NS_ENSURE_STATE(os);
michael@0 1959
michael@0 1960 // If shutdown happens in the same mainthread loop as init, observers could
michael@0 1961 // handle the places-init-complete notification after xpcom-shutdown, when
michael@0 1962 // the connection does not exist anymore. Removing those observers would
michael@0 1963 // be less expensive but may cause their RemoveObserver calls to throw.
michael@0 1964 // Thus notify the topic now, so they stop listening for it.
michael@0 1965 nsCOMPtr<nsISimpleEnumerator> e;
michael@0 1966 if (NS_SUCCEEDED(os->EnumerateObservers(TOPIC_PLACES_INIT_COMPLETE,
michael@0 1967 getter_AddRefs(e))) && e) {
michael@0 1968 bool hasMore = false;
michael@0 1969 while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
michael@0 1970 nsCOMPtr<nsISupports> supports;
michael@0 1971 if (NS_SUCCEEDED(e->GetNext(getter_AddRefs(supports)))) {
michael@0 1972 nsCOMPtr<nsIObserver> observer = do_QueryInterface(supports);
michael@0 1973 (void)observer->Observe(observer, TOPIC_PLACES_INIT_COMPLETE, nullptr);
michael@0 1974 }
michael@0 1975 }
michael@0 1976 }
michael@0 1977
michael@0 1978 // Notify all Places users that we are about to shutdown.
michael@0 1979 (void)os->NotifyObservers(nullptr, TOPIC_PLACES_SHUTDOWN, nullptr);
michael@0 1980 }
michael@0 1981
michael@0 1982 else if (strcmp(aTopic, TOPIC_PROFILE_BEFORE_CHANGE) == 0) {
michael@0 1983 // Tests simulating shutdown may cause re-entrance.
michael@0 1984 if (mShuttingDown) {
michael@0 1985 return NS_OK;
michael@0 1986 }
michael@0 1987
michael@0 1988 // Fire internal shutdown notifications.
michael@0 1989 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
michael@0 1990 if (os) {
michael@0 1991 (void)os->NotifyObservers(nullptr, TOPIC_PLACES_WILL_CLOSE_CONNECTION, nullptr);
michael@0 1992 }
michael@0 1993
michael@0 1994 #ifdef DEBUG
michael@0 1995 { // Sanity check for missing guids.
michael@0 1996 bool haveNullGuids = false;
michael@0 1997 nsCOMPtr<mozIStorageStatement> stmt;
michael@0 1998
michael@0 1999 nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 2000 "SELECT 1 "
michael@0 2001 "FROM moz_places "
michael@0 2002 "WHERE guid IS NULL "
michael@0 2003 ), getter_AddRefs(stmt));
michael@0 2004 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2005 rv = stmt->ExecuteStep(&haveNullGuids);
michael@0 2006 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2007 MOZ_ASSERT(!haveNullGuids && "Found a page without a GUID!");
michael@0 2008
michael@0 2009 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 2010 "SELECT 1 "
michael@0 2011 "FROM moz_bookmarks "
michael@0 2012 "WHERE guid IS NULL "
michael@0 2013 ), getter_AddRefs(stmt));
michael@0 2014 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2015 rv = stmt->ExecuteStep(&haveNullGuids);
michael@0 2016 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2017 MOZ_ASSERT(!haveNullGuids && "Found a bookmark without a GUID!");
michael@0 2018
michael@0 2019 rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
michael@0 2020 "SELECT 1 "
michael@0 2021 "FROM moz_favicons "
michael@0 2022 "WHERE guid IS NULL "
michael@0 2023 ), getter_AddRefs(stmt));
michael@0 2024 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2025 rv = stmt->ExecuteStep(&haveNullGuids);
michael@0 2026 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2027 MOZ_ASSERT(!haveNullGuids && "Found a favicon without a GUID!");
michael@0 2028 }
michael@0 2029 #endif
michael@0 2030
michael@0 2031 // As the last step in the shutdown path, finalize the database handle.
michael@0 2032 Shutdown();
michael@0 2033 }
michael@0 2034
michael@0 2035 return NS_OK;
michael@0 2036 }
michael@0 2037
michael@0 2038 } // namespace places
michael@0 2039 } // namespace mozilla

mercurial