Wed, 31 Dec 2014 06:09:35 +0100
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(¤tSchemaVersion); |
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 |