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(¤tSchemaVersion); 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