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