LCOV - code coverage report
Current view: top level - toolkit/components/places - Database.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 292 1343 21.7 %
Date: 2017-07-14 16:53:18 Functions: 25 64 39.1 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : #include "mozilla/ScopeExit.h"
       9             : 
      10             : #include "Database.h"
      11             : 
      12             : #include "nsIAnnotationService.h"
      13             : #include "nsINavBookmarksService.h"
      14             : #include "nsIInterfaceRequestorUtils.h"
      15             : #include "nsIFile.h"
      16             : #include "nsIWritablePropertyBag2.h"
      17             : 
      18             : #include "nsNavHistory.h"
      19             : #include "nsPlacesTables.h"
      20             : #include "nsPlacesIndexes.h"
      21             : #include "nsPlacesTriggers.h"
      22             : #include "nsPlacesMacros.h"
      23             : #include "nsVariant.h"
      24             : #include "SQLFunctions.h"
      25             : #include "Helpers.h"
      26             : #include "nsFaviconService.h"
      27             : 
      28             : #include "nsAppDirectoryServiceDefs.h"
      29             : #include "nsDirectoryServiceUtils.h"
      30             : #include "prenv.h"
      31             : #include "prsystem.h"
      32             : #include "nsPrintfCString.h"
      33             : #include "mozilla/Preferences.h"
      34             : #include "mozilla/Services.h"
      35             : #include "mozilla/Unused.h"
      36             : #include "prtime.h"
      37             : 
      38             : #include "nsXULAppAPI.h"
      39             : 
      40             : // Time between corrupt database backups.
      41             : #define RECENT_BACKUP_TIME_MICROSEC (int64_t)86400 * PR_USEC_PER_SEC // 24H
      42             : 
      43             : // Filename of the database.
      44             : #define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite")
      45             : // Filename used to backup corrupt databases.
      46             : #define DATABASE_CORRUPT_FILENAME NS_LITERAL_STRING("places.sqlite.corrupt")
      47             : // Filename of the icons database.
      48             : #define DATABASE_FAVICONS_FILENAME NS_LITERAL_STRING("favicons.sqlite")
      49             : 
      50             : // Set when the database file was found corrupt by a previous maintenance.
      51             : #define PREF_FORCE_DATABASE_REPLACEMENT "places.database.replaceOnStartup"
      52             : 
      53             : // Set to specify the size of the places database growth increments in kibibytes
      54             : #define PREF_GROWTH_INCREMENT_KIB "places.database.growthIncrementKiB"
      55             : 
      56             : // Set to disable the default robust storage and use volatile, in-memory
      57             : // storage without robust transaction flushing guarantees. This makes
      58             : // SQLite use much less I/O at the cost of losing data when things crash.
      59             : // The pref is only honored if an environment variable is set. The env
      60             : // variable is intentionally named something scary to help prevent someone
      61             : // from thinking it is a useful performance optimization they should enable.
      62             : #define PREF_DISABLE_DURABILITY "places.database.disableDurability"
      63             : #define ENV_ALLOW_CORRUPTION "ALLOW_PLACES_DATABASE_TO_LOSE_DATA_AND_BECOME_CORRUPT"
      64             : 
      65             : // The maximum url length we can store in history.
      66             : // We do not add to history URLs longer than this value.
      67             : #define PREF_HISTORY_MAXURLLEN "places.history.maxUrlLength"
      68             : // This number is mostly a guess based on various facts:
      69             : // * IE didn't support urls longer than 2083 chars
      70             : // * Sitemaps protocol used to support a maximum of 2048 chars
      71             : // * Various SEO guides suggest to not go over 2000 chars
      72             : // * Various apps/services are known to have issues over 2000 chars
      73             : // * RFC 2616 - HTTP/1.1 suggests being cautious about depending
      74             : //   on URI lengths above 255 bytes
      75             : #define PREF_HISTORY_MAXURLLEN_DEFAULT 2000
      76             : 
      77             : // Maximum size for the WAL file.
      78             : // For performance reasons this should be as large as possible, so that more
      79             : // transactions can fit into it, and the checkpoint cost is paid less often.
      80             : // At the same time, since we use synchronous = NORMAL, an fsync happens only
      81             : // at checkpoint time, so we don't want the WAL to grow too much and risk to
      82             : // lose all the contained transactions on a crash.
      83             : #define DATABASE_MAX_WAL_BYTES 2048000
      84             : 
      85             : // Since exceeding the journal limit will cause a truncate, we allow a slightly
      86             : // larger limit than DATABASE_MAX_WAL_BYTES to reduce the number of truncates.
      87             : // This is the number of bytes the journal can grow over the maximum wal size
      88             : // before being truncated.
      89             : #define DATABASE_JOURNAL_OVERHEAD_BYTES 2048000
      90             : 
      91             : #define BYTES_PER_KIBIBYTE 1024
      92             : 
      93             : // How much time Sqlite can wait before returning a SQLITE_BUSY error.
      94             : #define DATABASE_BUSY_TIMEOUT_MS 100
      95             : 
      96             : // Old Sync GUID annotation.
      97             : #define SYNCGUID_ANNO NS_LITERAL_CSTRING("sync/guid")
      98             : 
      99             : // Places string bundle, contains internationalized bookmark root names.
     100             : #define PLACES_BUNDLE "chrome://places/locale/places.properties"
     101             : 
     102             : // Livemarks annotations.
     103             : #define LMANNO_FEEDURI "livemark/feedURI"
     104             : #define LMANNO_SITEURI "livemark/siteURI"
     105             : 
     106             : #define MOBILE_ROOT_GUID "mobile______"
     107             : #define MOBILE_ROOT_ANNO "mobile/bookmarksRoot"
     108             : 
     109             : // We use a fixed title for the mobile root to avoid marking the database as
     110             : // corrupt if we can't look up the localized title in the string bundle. Sync
     111             : // sets the title to the localized version when it creates the left pane query.
     112             : #define MOBILE_ROOT_TITLE "mobile"
     113             : 
     114             : using namespace mozilla;
     115             : 
     116             : namespace mozilla {
     117             : namespace places {
     118             : 
     119             : namespace {
     120             : 
     121             : ////////////////////////////////////////////////////////////////////////////////
     122             : //// Helpers
     123             : 
     124             : /**
     125             :  * Checks whether exists a database backup created not longer than
     126             :  * RECENT_BACKUP_TIME_MICROSEC ago.
     127             :  */
     128             : bool
     129           0 : hasRecentCorruptDB()
     130             : {
     131           0 :   MOZ_ASSERT(NS_IsMainThread());
     132             : 
     133           0 :   nsCOMPtr<nsIFile> profDir;
     134           0 :   NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, getter_AddRefs(profDir));
     135           0 :   NS_ENSURE_TRUE(profDir, false);
     136           0 :   nsCOMPtr<nsISimpleEnumerator> entries;
     137           0 :   profDir->GetDirectoryEntries(getter_AddRefs(entries));
     138           0 :   NS_ENSURE_TRUE(entries, false);
     139             :   bool hasMore;
     140           0 :   while (NS_SUCCEEDED(entries->HasMoreElements(&hasMore)) && hasMore) {
     141           0 :     nsCOMPtr<nsISupports> next;
     142           0 :     entries->GetNext(getter_AddRefs(next));
     143           0 :     NS_ENSURE_TRUE(next, false);
     144           0 :     nsCOMPtr<nsIFile> currFile = do_QueryInterface(next);
     145           0 :     NS_ENSURE_TRUE(currFile, false);
     146             : 
     147           0 :     nsAutoString leafName;
     148           0 :     if (NS_SUCCEEDED(currFile->GetLeafName(leafName)) &&
     149           0 :         leafName.Length() >= DATABASE_CORRUPT_FILENAME.Length() &&
     150           0 :         leafName.Find(".corrupt", DATABASE_FILENAME.Length()) != -1) {
     151           0 :       PRTime lastMod = 0;
     152           0 :       currFile->GetLastModifiedTime(&lastMod);
     153           0 :       NS_ENSURE_TRUE(lastMod > 0, false);
     154           0 :       return (PR_Now() - lastMod) > RECENT_BACKUP_TIME_MICROSEC;
     155             :     }
     156             :   }
     157           0 :   return false;
     158             : }
     159             : 
     160             : /**
     161             :  * Updates sqlite_stat1 table through ANALYZE.
     162             :  * Since also nsPlacesExpiration.js executes ANALYZE, the analyzed tables
     163             :  * must be the same in both components.  So ensure they are in sync.
     164             :  *
     165             :  * @param aDBConn
     166             :  *        The database connection.
     167             :  */
     168             : nsresult
     169           0 : updateSQLiteStatistics(mozIStorageConnection* aDBConn)
     170             : {
     171           0 :   MOZ_ASSERT(NS_IsMainThread());
     172           0 :   nsCOMPtr<mozIStorageAsyncStatement> analyzePlacesStmt;
     173           0 :   aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
     174             :     "ANALYZE moz_places"
     175           0 :   ), getter_AddRefs(analyzePlacesStmt));
     176           0 :   NS_ENSURE_STATE(analyzePlacesStmt);
     177           0 :   nsCOMPtr<mozIStorageAsyncStatement> analyzeBookmarksStmt;
     178           0 :   aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
     179             :     "ANALYZE moz_bookmarks"
     180           0 :   ), getter_AddRefs(analyzeBookmarksStmt));
     181           0 :   NS_ENSURE_STATE(analyzeBookmarksStmt);
     182           0 :   nsCOMPtr<mozIStorageAsyncStatement> analyzeVisitsStmt;
     183           0 :   aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
     184             :     "ANALYZE moz_historyvisits"
     185           0 :   ), getter_AddRefs(analyzeVisitsStmt));
     186           0 :   NS_ENSURE_STATE(analyzeVisitsStmt);
     187           0 :   nsCOMPtr<mozIStorageAsyncStatement> analyzeInputStmt;
     188           0 :   aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
     189             :     "ANALYZE moz_inputhistory"
     190           0 :   ), getter_AddRefs(analyzeInputStmt));
     191           0 :   NS_ENSURE_STATE(analyzeInputStmt);
     192             : 
     193             :   mozIStorageBaseStatement *stmts[] = {
     194             :     analyzePlacesStmt,
     195             :     analyzeBookmarksStmt,
     196             :     analyzeVisitsStmt,
     197             :     analyzeInputStmt
     198           0 :   };
     199             : 
     200           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
     201           0 :   (void)aDBConn->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
     202           0 :                               getter_AddRefs(ps));
     203           0 :   return NS_OK;
     204             : }
     205             : 
     206             : /**
     207             :  * Sets the connection journal mode to one of the JOURNAL_* types.
     208             :  *
     209             :  * @param aDBConn
     210             :  *        The database connection.
     211             :  * @param aJournalMode
     212             :  *        One of the JOURNAL_* types.
     213             :  * @returns the current journal mode.
     214             :  * @note this may return a different journal mode than the required one, since
     215             :  *       setting it may fail.
     216             :  */
     217             : enum JournalMode
     218           1 : SetJournalMode(nsCOMPtr<mozIStorageConnection>& aDBConn,
     219             :                enum JournalMode aJournalMode)
     220             : {
     221           1 :   MOZ_ASSERT(NS_IsMainThread());
     222           2 :   nsAutoCString journalMode;
     223           1 :   switch (aJournalMode) {
     224             :     default:
     225           0 :       MOZ_FALLTHROUGH_ASSERT("Trying to set an unknown journal mode.");
     226             :       // Fall through to the default DELETE journal.
     227             :     case JOURNAL_DELETE:
     228           0 :       journalMode.AssignLiteral("delete");
     229           0 :       break;
     230             :     case JOURNAL_TRUNCATE:
     231           0 :       journalMode.AssignLiteral("truncate");
     232           0 :       break;
     233             :     case JOURNAL_MEMORY:
     234           0 :       journalMode.AssignLiteral("memory");
     235           0 :       break;
     236             :     case JOURNAL_WAL:
     237           1 :       journalMode.AssignLiteral("wal");
     238           1 :       break;
     239             :   }
     240             : 
     241           2 :   nsCOMPtr<mozIStorageStatement> statement;
     242           2 :   nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA journal_mode = ");
     243           1 :   query.Append(journalMode);
     244           1 :   aDBConn->CreateStatement(query, getter_AddRefs(statement));
     245           1 :   NS_ENSURE_TRUE(statement, JOURNAL_DELETE);
     246             : 
     247           1 :   bool hasResult = false;
     248           2 :   if (NS_SUCCEEDED(statement->ExecuteStep(&hasResult)) && hasResult &&
     249           1 :       NS_SUCCEEDED(statement->GetUTF8String(0, journalMode))) {
     250           1 :     if (journalMode.EqualsLiteral("delete")) {
     251           0 :       return JOURNAL_DELETE;
     252             :     }
     253           1 :     if (journalMode.EqualsLiteral("truncate")) {
     254           0 :       return JOURNAL_TRUNCATE;
     255             :     }
     256           1 :     if (journalMode.EqualsLiteral("memory")) {
     257           0 :       return JOURNAL_MEMORY;
     258             :     }
     259           1 :     if (journalMode.EqualsLiteral("wal")) {
     260           1 :       return JOURNAL_WAL;
     261             :     }
     262           0 :     MOZ_ASSERT(false, "Got an unknown journal mode.");
     263             :   }
     264             : 
     265           0 :   return JOURNAL_DELETE;
     266             : }
     267             : 
     268             : nsresult
     269           0 : CreateRoot(nsCOMPtr<mozIStorageConnection>& aDBConn,
     270             :            const nsCString& aRootName, const nsCString& aGuid,
     271             :            const nsXPIDLString& titleString)
     272             : {
     273           0 :   MOZ_ASSERT(NS_IsMainThread());
     274             : 
     275             :   // The position of the new item in its folder.
     276             :   static int32_t itemPosition = 0;
     277             : 
     278             :   // A single creation timestamp for all roots so that the root folder's
     279             :   // last modification time isn't earlier than its childrens' creation time.
     280             :   static PRTime timestamp = 0;
     281           0 :   if (!timestamp)
     282           0 :     timestamp = RoundedPRNow();
     283             : 
     284             :   // Create a new bookmark folder for the root.
     285           0 :   nsCOMPtr<mozIStorageStatement> stmt;
     286           0 :   nsresult rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
     287             :     "INSERT INTO moz_bookmarks "
     288             :       "(type, position, title, dateAdded, lastModified, guid, parent, "
     289             :        "syncChangeCounter, syncStatus) "
     290             :     "VALUES (:item_type, :item_position, :item_title,"
     291             :             ":date_added, :last_modified, :guid, "
     292             :             "IFNULL((SELECT id FROM moz_bookmarks WHERE parent = 0), 0), "
     293             :             "1, :sync_status)"
     294           0 :   ), getter_AddRefs(stmt));
     295           0 :   if (NS_FAILED(rv)) return rv;
     296             : 
     297           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"),
     298           0 :                              nsINavBookmarksService::TYPE_FOLDER);
     299           0 :   if (NS_FAILED(rv)) return rv;
     300           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("item_position"), itemPosition);
     301           0 :   if (NS_FAILED(rv)) return rv;
     302           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
     303           0 :                                   NS_ConvertUTF16toUTF8(titleString));
     304           0 :   if (NS_FAILED(rv)) return rv;
     305           0 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("date_added"), timestamp);
     306           0 :   if (NS_FAILED(rv)) return rv;
     307           0 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("last_modified"), timestamp);
     308           0 :   if (NS_FAILED(rv)) return rv;
     309           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), aGuid);
     310           0 :   if (NS_FAILED(rv)) return rv;
     311           0 :   rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("sync_status"),
     312           0 :                              nsINavBookmarksService::SYNC_STATUS_NEW);
     313           0 :   if (NS_FAILED(rv)) return rv;
     314           0 :   rv = stmt->Execute();
     315           0 :   if (NS_FAILED(rv)) return rv;
     316             : 
     317             :   // The 'places' root is a folder containing the other roots.
     318             :   // The first bookmark in a folder has position 0.
     319           0 :   if (!aRootName.EqualsLiteral("places"))
     320           0 :     ++itemPosition;
     321             : 
     322           0 :   return NS_OK;
     323             : }
     324             : 
     325             : nsresult
     326           1 : SetupDurability(nsCOMPtr<mozIStorageConnection>& aDBConn, int32_t aDBPageSize) {
     327             :   nsresult rv;
     328           1 :   if (PR_GetEnv(ENV_ALLOW_CORRUPTION) &&
     329           0 :       Preferences::GetBool(PREF_DISABLE_DURABILITY, false)) {
     330             :     // Volatile storage was requested. Use the in-memory journal (no
     331             :     // filesystem I/O) and don't sync the filesystem after writing.
     332           0 :     SetJournalMode(aDBConn, JOURNAL_MEMORY);
     333           0 :     rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     334           0 :       "PRAGMA synchronous = OFF"));
     335           0 :     NS_ENSURE_SUCCESS(rv, rv);
     336             :   } else {
     337             :     // Be sure to set journal mode after page_size.  WAL would prevent the change
     338             :     // otherwise.
     339           1 :     if (JOURNAL_WAL == SetJournalMode(aDBConn, JOURNAL_WAL)) {
     340             :       // Set the WAL journal size limit.
     341             :       int32_t checkpointPages =
     342           1 :         static_cast<int32_t>(DATABASE_MAX_WAL_BYTES / aDBPageSize);
     343           2 :       nsAutoCString checkpointPragma("PRAGMA wal_autocheckpoint = ");
     344           1 :       checkpointPragma.AppendInt(checkpointPages);
     345           1 :       rv = aDBConn->ExecuteSimpleSQL(checkpointPragma);
     346           1 :       NS_ENSURE_SUCCESS(rv, rv);
     347             :     }
     348             :     else {
     349             :       // Ignore errors, if we fail here the database could be considered corrupt
     350             :       // and we won't be able to go on, even if it's just matter of a bogus file
     351             :       // system.  The default mode (DELETE) will be fine in such a case.
     352           0 :       (void)SetJournalMode(aDBConn, JOURNAL_TRUNCATE);
     353             : 
     354             :       // Set synchronous to FULL to ensure maximum data integrity, even in
     355             :       // case of crashes or unclean shutdowns.
     356           0 :       rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     357           0 :           "PRAGMA synchronous = FULL"));
     358           0 :       NS_ENSURE_SUCCESS(rv, rv);
     359             :     }
     360             :   }
     361             : 
     362             :   // The journal is usually free to grow for performance reasons, but it never
     363             :   // shrinks back.  Since the space taken may be problematic, limit its size.
     364           2 :   nsAutoCString journalSizePragma("PRAGMA journal_size_limit = ");
     365           1 :   journalSizePragma.AppendInt(DATABASE_MAX_WAL_BYTES + DATABASE_JOURNAL_OVERHEAD_BYTES);
     366           1 :   (void)aDBConn->ExecuteSimpleSQL(journalSizePragma);
     367             : 
     368             :   // Grow places in |growthIncrementKiB| increments to limit fragmentation on disk.
     369             :   // By default, it's 5 MB.
     370             :   int32_t growthIncrementKiB =
     371           1 :     Preferences::GetInt(PREF_GROWTH_INCREMENT_KIB, 5 * BYTES_PER_KIBIBYTE);
     372           1 :   if (growthIncrementKiB > 0) {
     373           1 :     (void)aDBConn->SetGrowthIncrement(growthIncrementKiB * BYTES_PER_KIBIBYTE, EmptyCString());
     374             :   }
     375           1 :   return NS_OK;
     376             : }
     377             : 
     378             : nsresult
     379           1 : AttachDatabase(nsCOMPtr<mozIStorageConnection>& aDBConn,
     380             :                const nsACString& aPath,
     381             :                const nsACString& aName) {
     382           3 :   nsresult rv = aDBConn->ExecuteSimpleSQL(
     383           3 :     NS_LITERAL_CSTRING("ATTACH DATABASE '") + aPath + NS_LITERAL_CSTRING("' AS ") + aName);
     384           1 :   NS_ENSURE_SUCCESS(rv, rv);
     385             : 
     386             :   // The journal limit must be set apart for each database.
     387           2 :   nsAutoCString journalSizePragma("PRAGMA favicons.journal_size_limit = ");
     388           1 :   journalSizePragma.AppendInt(DATABASE_MAX_WAL_BYTES + DATABASE_JOURNAL_OVERHEAD_BYTES);
     389           1 :   Unused << aDBConn->ExecuteSimpleSQL(journalSizePragma);
     390             : 
     391           1 :   return NS_OK;
     392             : }
     393             : 
     394             : } // namespace
     395             : 
     396             : ////////////////////////////////////////////////////////////////////////////////
     397             : //// Database
     398             : 
     399          10 : PLACES_FACTORY_SINGLETON_IMPLEMENTATION(Database, gDatabase)
     400             : 
     401          19 : NS_IMPL_ISUPPORTS(Database
     402             : , nsIObserver
     403             : , nsISupportsWeakReference
     404             : )
     405             : 
     406           1 : Database::Database()
     407             :   : mMainThreadStatements(mMainConn)
     408             :   , mMainThreadAsyncStatements(mMainConn)
     409             :   , mAsyncThreadStatements(mMainConn)
     410             :   , mDBPageSize(0)
     411             :   , mDatabaseStatus(nsINavHistoryService::DATABASE_STATUS_OK)
     412             :   , mClosed(false)
     413           1 :   , mClientsShutdown(new ClientsShutdownBlocker())
     414           1 :   , mConnectionShutdown(new ConnectionShutdownBlocker(this))
     415             :   , mMaxUrlLength(0)
     416           3 :   , mCacheObservers(TOPIC_PLACES_INIT_COMPLETE)
     417             : {
     418           1 :   MOZ_ASSERT(!XRE_IsContentProcess(),
     419             :              "Cannot instantiate Places in the content process");
     420             :   // Attempting to create two instances of the service?
     421           1 :   MOZ_ASSERT(!gDatabase);
     422           1 :   gDatabase = this;
     423           1 : }
     424             : 
     425             : already_AddRefed<nsIAsyncShutdownClient>
     426           1 : Database::GetProfileChangeTeardownPhase()
     427             : {
     428           2 :   nsCOMPtr<nsIAsyncShutdownService> asyncShutdownSvc = services::GetAsyncShutdown();
     429           1 :   MOZ_ASSERT(asyncShutdownSvc);
     430           1 :   if (NS_WARN_IF(!asyncShutdownSvc)) {
     431           0 :     return nullptr;
     432             :   }
     433             : 
     434             :   // Consumers of Places should shutdown before us, at profile-change-teardown.
     435           2 :   nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
     436           1 :   DebugOnly<nsresult> rv = asyncShutdownSvc->
     437           2 :     GetProfileChangeTeardown(getter_AddRefs(shutdownPhase));
     438           1 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
     439           1 :   return shutdownPhase.forget();
     440             : }
     441             : 
     442             : already_AddRefed<nsIAsyncShutdownClient>
     443           1 : Database::GetProfileBeforeChangePhase()
     444             : {
     445           2 :   nsCOMPtr<nsIAsyncShutdownService> asyncShutdownSvc = services::GetAsyncShutdown();
     446           1 :   MOZ_ASSERT(asyncShutdownSvc);
     447           1 :   if (NS_WARN_IF(!asyncShutdownSvc)) {
     448           0 :     return nullptr;
     449             :   }
     450             : 
     451             :   // Consumers of Places should shutdown before us, at profile-change-teardown.
     452           2 :   nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase;
     453           1 :   DebugOnly<nsresult> rv = asyncShutdownSvc->
     454           2 :     GetProfileBeforeChange(getter_AddRefs(shutdownPhase));
     455           1 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
     456           1 :   return shutdownPhase.forget();
     457             : }
     458             : 
     459           0 : Database::~Database()
     460             : {
     461           0 : }
     462             : 
     463             : bool
     464          10 : Database::IsShutdownStarted() const
     465             : {
     466          10 :   if (!mConnectionShutdown) {
     467             :     // We have already broken the cycle between `this` and `mConnectionShutdown`.
     468           0 :     return true;
     469             :   }
     470          10 :   return mConnectionShutdown->IsStarted();
     471             : }
     472             : 
     473             : already_AddRefed<mozIStorageAsyncStatement>
     474           1 : Database::GetAsyncStatement(const nsACString& aQuery)
     475             : {
     476           1 :   if (IsShutdownStarted() || NS_FAILED(EnsureConnection())) {
     477           0 :     return nullptr;
     478             :   }
     479             : 
     480           1 :   MOZ_ASSERT(NS_IsMainThread());
     481           1 :   return mMainThreadAsyncStatements.GetCachedStatement(aQuery);
     482             : }
     483             : 
     484             : already_AddRefed<mozIStorageStatement>
     485           8 : Database::GetStatement(const nsACString& aQuery)
     486             : {
     487           8 :   if (IsShutdownStarted()) {
     488           0 :     return nullptr;
     489             :   }
     490           8 :   if (NS_IsMainThread()) {
     491           0 :     if (NS_FAILED(EnsureConnection())) {
     492           0 :       return nullptr;
     493             :     }
     494           0 :     return mMainThreadStatements.GetCachedStatement(aQuery);
     495             :   }
     496             :   // In the async case, the connection must have been started on the main-thread
     497             :   // already.
     498           8 :   MOZ_ASSERT(mMainConn);
     499           8 :   return mAsyncThreadStatements.GetCachedStatement(aQuery);
     500             : }
     501             : 
     502             : already_AddRefed<nsIAsyncShutdownClient>
     503           0 : Database::GetClientsShutdown()
     504             : {
     505           0 :   MOZ_ASSERT(mClientsShutdown);
     506           0 :   return mClientsShutdown->GetClient();
     507             : }
     508             : 
     509             : // static
     510             : already_AddRefed<Database>
     511           9 : Database::GetDatabase()
     512             : {
     513           9 :   if (PlacesShutdownBlocker::IsStarted()) {
     514           0 :     return nullptr;
     515             :   }
     516           9 :   return GetSingleton();
     517             : }
     518             : 
     519             : nsresult
     520           1 : Database::Init()
     521             : {
     522           1 :   MOZ_ASSERT(NS_IsMainThread());
     523             : 
     524             :   // DO NOT FAIL HERE, otherwise we would never break the cycle between this
     525             :   // object and the shutdown blockers, causing unexpected leaks.
     526             : 
     527             :   {
     528             :     // First of all Places clients should block profile-change-teardown.
     529           2 :     nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetProfileChangeTeardownPhase();
     530           1 :     MOZ_ASSERT(shutdownPhase);
     531           1 :     if (shutdownPhase) {
     532           5 :       DebugOnly<nsresult> rv = shutdownPhase->AddBlocker(
     533           1 :         static_cast<nsIAsyncShutdownBlocker*>(mClientsShutdown.get()),
     534           2 :         NS_LITERAL_STRING(__FILE__),
     535             :         __LINE__,
     536           5 :         NS_LITERAL_STRING(""));
     537           1 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     538             :     }
     539             :   }
     540             : 
     541             :   {
     542             :     // Then connection closing should block profile-before-change.
     543           2 :     nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetProfileBeforeChangePhase();
     544           1 :     MOZ_ASSERT(shutdownPhase);
     545           1 :     if (shutdownPhase) {
     546           5 :       DebugOnly<nsresult> rv = shutdownPhase->AddBlocker(
     547           1 :         static_cast<nsIAsyncShutdownBlocker*>(mConnectionShutdown.get()),
     548           2 :         NS_LITERAL_STRING(__FILE__),
     549             :         __LINE__,
     550           5 :         NS_LITERAL_STRING(""));
     551           1 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
     552             :     }
     553             :   }
     554             : 
     555             :   // Finally observe profile shutdown notifications.
     556           2 :   nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
     557           1 :   if (os) {
     558           1 :     (void)os->AddObserver(this, TOPIC_PROFILE_CHANGE_TEARDOWN, true);
     559             :   }
     560           2 :   return NS_OK;
     561             : }
     562             : 
     563             : nsresult
     564          14 : Database::EnsureConnection()
     565             : {
     566             :   // Run this only once.
     567          15 :   if (mMainConn ||
     568          15 :       mDatabaseStatus == nsINavHistoryService::DATABASE_STATUS_LOCKED) {
     569          13 :     return NS_OK;
     570             :   }
     571             :   // Don't try to create a database too late.
     572           1 :   if (IsShutdownStarted()) {
     573           0 :     return NS_ERROR_FAILURE;
     574             :   }
     575             : 
     576           1 :   MOZ_ASSERT(NS_IsMainThread(),
     577             :              "Database initialization must happen on the main-thread");
     578             : 
     579             :   {
     580           1 :     bool initSucceeded = false;
     581           1 :     auto notify = MakeScopeExit([&] () {
     582             :       // If the database connection cannot be opened, it may just be locked
     583             :       // by third parties.  Set a locked state.
     584           1 :       if (!initSucceeded) {
     585           1 :         mMainConn = nullptr;
     586           0 :         mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_LOCKED;
     587             :       }
     588             :       // Notify at the next tick, to avoid re-entrancy problems.
     589           2 :       NS_DispatchToMainThread(
     590           3 :         NewRunnableMethod("places::Database::EnsureConnection()",
     591             :                           this, &Database::NotifyConnectionInitalized)
     592           1 :       );
     593           3 :     });
     594             : 
     595             :     nsCOMPtr<mozIStorageService> storage =
     596           2 :       do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
     597           1 :     NS_ENSURE_STATE(storage);
     598             : 
     599             :     // Init the database file and connect to it.
     600           1 :     bool databaseCreated = false;
     601           1 :     nsresult rv = InitDatabaseFile(storage, &databaseCreated);
     602           1 :     if (NS_SUCCEEDED(rv) && databaseCreated) {
     603           0 :       mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CREATE;
     604             :     }
     605           1 :     else if (rv == NS_ERROR_FILE_CORRUPTED) {
     606             :       // The database is corrupt, backup and replace it with a new one.
     607           0 :       mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CORRUPT;
     608           0 :       rv = BackupAndReplaceDatabaseFile(storage);
     609             :       // Fallback to catch-all handler.
     610             :     }
     611           1 :     NS_ENSURE_SUCCESS(rv, rv);
     612             : 
     613             :     // Ensure the icons database exists.
     614           1 :     rv = EnsureFaviconsDatabaseFile(storage);
     615           1 :     NS_ENSURE_SUCCESS(rv, rv);
     616             : 
     617             :     // Initialize the database schema.  In case of failure the existing schema is
     618             :     // is corrupt or incoherent, thus the database should be replaced.
     619           1 :     bool databaseMigrated = false;
     620           1 :     rv = SetupDatabaseConnection(storage);
     621           1 :     if (NS_SUCCEEDED(rv)) {
     622             :       // Failing to initialize the schema always indicates a corruption.
     623           1 :       if (NS_FAILED(InitSchema(&databaseMigrated))) {
     624           0 :         rv = NS_ERROR_FILE_CORRUPTED;
     625             :       }
     626             :     }
     627           1 :     if (NS_WARN_IF(NS_FAILED(rv))) {
     628           0 :       mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_CORRUPT;
     629             :       // Some errors may not indicate a database corruption, for those cases we
     630             :       // just bail out without throwing away a possibly valid places.sqlite.
     631           0 :       if (rv == NS_ERROR_FILE_CORRUPTED) {
     632           0 :         rv = BackupAndReplaceDatabaseFile(storage);
     633           0 :         NS_ENSURE_SUCCESS(rv, rv);
     634             :         // Try to initialize the new database again.
     635           0 :         rv = SetupDatabaseConnection(storage);
     636           0 :         NS_ENSURE_SUCCESS(rv, rv);
     637           0 :         rv = InitSchema(&databaseMigrated);
     638             :       }
     639             :       // Bail out if we couldn't fix the database.
     640           0 :       NS_ENSURE_SUCCESS(rv, rv);
     641             :     }
     642             : 
     643           1 :     if (databaseMigrated) {
     644           0 :       mDatabaseStatus = nsINavHistoryService::DATABASE_STATUS_UPGRADED;
     645             :     }
     646             : 
     647           2 :     if (mDatabaseStatus == nsINavHistoryService::DATABASE_STATUS_UPGRADED ||
     648           1 :         mDatabaseStatus == nsINavHistoryService::DATABASE_STATUS_CREATE) {
     649           0 :       MOZ_ALWAYS_SUCCEEDS(updateSQLiteStatistics(mMainConn));
     650             :     }
     651             : 
     652             :     // Initialize here all the items that are not part of the on-disk database,
     653             :     // like views, temp triggers or temp tables.  The database should not be
     654             :     // considered corrupt if any of the following fails.
     655             : 
     656           1 :     rv = InitTempEntities();
     657           1 :     NS_ENSURE_SUCCESS(rv, rv);
     658             : 
     659           1 :     initSucceeded = true;
     660             :   }
     661           1 :   return NS_OK;
     662             : }
     663             : 
     664             : nsresult
     665           1 : Database::NotifyConnectionInitalized()
     666             : {
     667             :   // Notify about Places initialization.
     668           2 :   nsCOMArray<nsIObserver> entries;
     669           1 :   mCacheObservers.GetEntries(entries);
     670           2 :   for (int32_t idx = 0; idx < entries.Count(); ++idx) {
     671           1 :     MOZ_ALWAYS_SUCCEEDS(entries[idx]->Observe(nullptr, TOPIC_PLACES_INIT_COMPLETE, nullptr));
     672             :   }
     673           2 :   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     674           1 :   if (obs) {
     675           1 :     MOZ_ALWAYS_SUCCEEDS(obs->NotifyObservers(nullptr, TOPIC_PLACES_INIT_COMPLETE, nullptr));
     676             :   }
     677           2 :   return NS_OK;
     678             : }
     679             : 
     680             : nsresult
     681           1 : Database::EnsureFaviconsDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage)
     682             : {
     683           1 :   MOZ_ASSERT(NS_IsMainThread());
     684             : 
     685           2 :   nsCOMPtr<nsIFile> databaseFile;
     686           1 :   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     687           2 :                                        getter_AddRefs(databaseFile));
     688           1 :   NS_ENSURE_SUCCESS(rv, rv);
     689           1 :   rv = databaseFile->Append(DATABASE_FAVICONS_FILENAME);
     690           1 :   NS_ENSURE_SUCCESS(rv, rv);
     691             : 
     692           1 :   bool databaseFileExists = false;
     693           1 :   rv = databaseFile->Exists(&databaseFileExists);
     694           1 :   NS_ENSURE_SUCCESS(rv, rv);
     695             : 
     696           1 :   if (databaseFileExists) {
     697           1 :     return NS_OK;
     698             :   }
     699             : 
     700             :   // Open the database file, this will also create it.
     701           0 :   nsCOMPtr<mozIStorageConnection> conn;
     702           0 :   rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(conn));
     703           0 :   NS_ENSURE_SUCCESS(rv, rv);
     704             : 
     705             :   {
     706             :     // Ensure we'll close the connection when done.
     707           0 :     auto cleanup = MakeScopeExit([&] () {
     708             :       // We cannot use AsyncClose() here, because by the time we try to ATTACH
     709             :       // this database, its transaction could be still be running and that would
     710             :       // cause the ATTACH query to fail.
     711           0 :       MOZ_ALWAYS_TRUE(NS_SUCCEEDED(conn->Close()));
     712           0 :     });
     713             : 
     714             :     int32_t defaultPageSize;
     715           0 :     rv = conn->GetDefaultPageSize(&defaultPageSize);
     716           0 :     NS_ENSURE_SUCCESS(rv, rv);
     717           0 :     rv = SetupDurability(conn, defaultPageSize);
     718           0 :     NS_ENSURE_SUCCESS(rv, rv);
     719             : 
     720             :     // Enable incremental vacuum for this database. Since it will contain even
     721             :     // large blobs and can be cleared with history, it's worth to have it.
     722             :     // Note that it will be necessary to manually use PRAGMA incremental_vacuum.
     723           0 :     rv = conn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     724             :       "PRAGMA auto_vacuum = INCREMENTAL"
     725           0 :     ));
     726           0 :     NS_ENSURE_SUCCESS(rv, rv);
     727             : 
     728             :     // We are going to update the database, so everything from now on should be
     729             :     // in a transaction for performances.
     730           0 :     mozStorageTransaction transaction(conn, false);
     731           0 :     rv = conn->ExecuteSimpleSQL(CREATE_MOZ_ICONS);
     732           0 :     NS_ENSURE_SUCCESS(rv, rv);
     733           0 :     rv = conn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ICONS_ICONURLHASH);
     734           0 :     NS_ENSURE_SUCCESS(rv, rv);
     735           0 :     rv = conn->ExecuteSimpleSQL(CREATE_MOZ_PAGES_W_ICONS);
     736           0 :     NS_ENSURE_SUCCESS(rv, rv);
     737           0 :     rv = conn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PAGES_W_ICONS_ICONURLHASH);
     738           0 :     NS_ENSURE_SUCCESS(rv, rv);
     739           0 :     rv = conn->ExecuteSimpleSQL(CREATE_MOZ_ICONS_TO_PAGES);
     740           0 :     NS_ENSURE_SUCCESS(rv, rv);
     741           0 :     rv = transaction.Commit();
     742           0 :     NS_ENSURE_SUCCESS(rv, rv);
     743             : 
     744             :     // The scope exit will take care of closing the connection.
     745             :   }
     746             : 
     747           0 :   return NS_OK;
     748             : }
     749             : 
     750             : nsresult
     751           1 : Database::InitDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage,
     752             :                            bool* aNewDatabaseCreated)
     753             : {
     754           1 :   MOZ_ASSERT(NS_IsMainThread());
     755           1 :   *aNewDatabaseCreated = false;
     756             : 
     757           2 :   nsCOMPtr<nsIFile> databaseFile;
     758           1 :   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     759           2 :                                        getter_AddRefs(databaseFile));
     760           1 :   NS_ENSURE_SUCCESS(rv, rv);
     761           1 :   rv = databaseFile->Append(DATABASE_FILENAME);
     762           1 :   NS_ENSURE_SUCCESS(rv, rv);
     763             : 
     764           1 :   bool databaseFileExists = false;
     765           1 :   rv = databaseFile->Exists(&databaseFileExists);
     766           1 :   NS_ENSURE_SUCCESS(rv, rv);
     767             : 
     768           2 :   if (databaseFileExists &&
     769           1 :       Preferences::GetBool(PREF_FORCE_DATABASE_REPLACEMENT, false)) {
     770             :     // If this pref is set, Maintenance required a database replacement, due to
     771             :     // integrity corruption.
     772             :     // Be sure to clear the pref to avoid handling it more than once.
     773           0 :     (void)Preferences::ClearUser(PREF_FORCE_DATABASE_REPLACEMENT);
     774             : 
     775           0 :     return NS_ERROR_FILE_CORRUPTED;
     776             :   }
     777             : 
     778             :   // Open the database file.  If it does not exist a new one will be created.
     779             :   // Use an unshared connection, it will consume more memory but avoid shared
     780             :   // cache contentions across threads.
     781           1 :   rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
     782           1 :   NS_ENSURE_SUCCESS(rv, rv);
     783             : 
     784           1 :   *aNewDatabaseCreated = !databaseFileExists;
     785           1 :   return NS_OK;
     786             : }
     787             : 
     788             : nsresult
     789           0 : Database::BackupAndReplaceDatabaseFile(nsCOMPtr<mozIStorageService>& aStorage)
     790             : {
     791           0 :   MOZ_ASSERT(NS_IsMainThread());
     792           0 :   nsCOMPtr<nsIFile> profDir;
     793           0 :   nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     794           0 :                                        getter_AddRefs(profDir));
     795           0 :   NS_ENSURE_SUCCESS(rv, rv);
     796           0 :   nsCOMPtr<nsIFile> databaseFile;
     797           0 :   rv = profDir->Clone(getter_AddRefs(databaseFile));
     798           0 :   NS_ENSURE_SUCCESS(rv, rv);
     799           0 :   rv = databaseFile->Append(DATABASE_FILENAME);
     800           0 :   NS_ENSURE_SUCCESS(rv, rv);
     801             : 
     802             :   // If we have
     803             :   // already failed in the last 24 hours avoid to create another corrupt file,
     804             :   // since doing so, in some situation, could cause us to create a new corrupt
     805             :   // file at every try to access any Places service.  That is bad because it
     806             :   // would quickly fill the user's disk space without any notice.
     807           0 :   if (!hasRecentCorruptDB()) {
     808           0 :     nsCOMPtr<nsIFile> backup;
     809           0 :     (void)aStorage->BackupDatabaseFile(databaseFile, DATABASE_CORRUPT_FILENAME,
     810           0 :                                        profDir, getter_AddRefs(backup));
     811             :   }
     812             : 
     813             :   // If anything fails from this point on, we have a stale connection or
     814             :   // database file, and there's not much more we can do.
     815             :   // The only thing we can try to do is to replace the database on the next
     816             :   // startup, and report the problem through telemetry.
     817             :   {
     818             :     enum eCorruptDBReplaceStage : int8_t {
     819             :       stage_closing = 0,
     820             :       stage_removing,
     821             :       stage_reopening,
     822             :       stage_replaced
     823             :     };
     824           0 :     eCorruptDBReplaceStage stage = stage_closing;
     825           0 :     auto guard = MakeScopeExit([&]() {
     826           0 :       if (stage != stage_replaced) {
     827             :         // Reaching this point means the database is corrupt and we failed to
     828             :         // replace it.  For this session part of the application related to
     829             :         // bookmarks and history will misbehave.  The frontend may show a
     830             :         // "locked" notification to the user though.
     831             :         // Set up a pref to try replacing the database at the next startup.
     832           0 :         Preferences::SetBool(PREF_FORCE_DATABASE_REPLACEMENT, true);
     833             :       }
     834             :       // Report the corruption through telemetry.
     835           0 :       Telemetry::Accumulate(Telemetry::PLACES_DATABASE_CORRUPTION_HANDLING_STAGE,
     836           0 :                             static_cast<int8_t>(stage));
     837           0 :     });
     838             : 
     839             :     // Close database connection if open.
     840           0 :     if (mMainConn) {
     841           0 :       rv = mMainConn->SpinningSynchronousClose();
     842           0 :       NS_ENSURE_SUCCESS(rv, rv);
     843             :     }
     844             : 
     845             :     // Remove the broken database.
     846           0 :     stage = stage_removing;
     847           0 :     rv = databaseFile->Remove(false);
     848           0 :     if (NS_FAILED(rv) && rv != NS_ERROR_FILE_TARGET_DOES_NOT_EXIST) {
     849           0 :       return rv;
     850             :     }
     851             : 
     852             :     // Create a new database file.
     853             :     // Use an unshared connection, it will consume more memory but avoid shared
     854             :     // cache contentions across threads.
     855           0 :     stage = stage_reopening;
     856           0 :     rv = aStorage->OpenUnsharedDatabase(databaseFile, getter_AddRefs(mMainConn));
     857           0 :     NS_ENSURE_SUCCESS(rv, rv);
     858             : 
     859           0 :     stage = stage_replaced;
     860             :   }
     861             : 
     862           0 :   return NS_OK;
     863             : }
     864             : 
     865             : nsresult
     866           1 : Database::SetupDatabaseConnection(nsCOMPtr<mozIStorageService>& aStorage)
     867             : {
     868           1 :   MOZ_ASSERT(NS_IsMainThread());
     869             : 
     870             :   // WARNING: any statement executed before setting the journal mode must be
     871             :   // finalized, since SQLite doesn't allow changing the journal mode if there
     872             :   // is any outstanding statement.
     873             : 
     874             :   {
     875             :     // Get the page size.  This may be different than the default if the
     876             :     // database file already existed with a different page size.
     877           2 :     nsCOMPtr<mozIStorageStatement> statement;
     878           4 :     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
     879             :       MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"
     880           4 :     ), getter_AddRefs(statement));
     881           1 :     NS_ENSURE_SUCCESS(rv, rv);
     882           1 :     bool hasResult = false;
     883           1 :     rv = statement->ExecuteStep(&hasResult);
     884           1 :     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_FILE_CORRUPTED);
     885           1 :     rv = statement->GetInt32(0, &mDBPageSize);
     886           1 :     NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && mDBPageSize > 0, NS_ERROR_FILE_CORRUPTED);
     887             :   }
     888             : 
     889             :   // Ensure that temp tables are held in memory, not on disk.
     890           4 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     891             :     MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA temp_store = MEMORY")
     892           3 :   );
     893           1 :   NS_ENSURE_SUCCESS(rv, rv);
     894             : 
     895           1 :   rv = SetupDurability(mMainConn, mDBPageSize);
     896           1 :   NS_ENSURE_SUCCESS(rv, rv);
     897             : 
     898           2 :   nsAutoCString busyTimeoutPragma("PRAGMA busy_timeout = ");
     899           1 :   busyTimeoutPragma.AppendInt(DATABASE_BUSY_TIMEOUT_MS);
     900           1 :   (void)mMainConn->ExecuteSimpleSQL(busyTimeoutPragma);
     901             : 
     902             :   // Enable FOREIGN KEY support. This is a strict requirement.
     903           4 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     904             :     MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA foreign_keys = ON")
     905           3 :   );
     906           1 :   NS_ENSURE_SUCCESS(rv, NS_ERROR_FILE_CORRUPTED);
     907             : #ifdef DEBUG
     908             :   {
     909             :     // There are a few cases where setting foreign_keys doesn't work:
     910             :     //  * in the middle of a multi-statement transaction
     911             :     //  * if the SQLite library in use doesn't support them
     912             :     // Since we need foreign_keys, let's at least assert in debug mode.
     913           2 :     nsCOMPtr<mozIStorageStatement> stmt;
     914           4 :     mMainConn->CreateStatement(NS_LITERAL_CSTRING("PRAGMA foreign_keys"),
     915           4 :                             getter_AddRefs(stmt));
     916           1 :     bool hasResult = false;
     917           1 :     if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
     918           1 :       int32_t fkState = stmt->AsInt32(0);
     919           1 :       MOZ_ASSERT(fkState, "Foreign keys should be enabled");
     920             :     }
     921             :   }
     922             : #endif
     923             : 
     924             :   // Attach the favicons database to the main connection.
     925           2 :   nsCOMPtr<nsIFile> iconsFile;
     926           1 :   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
     927           2 :                               getter_AddRefs(iconsFile));
     928           1 :   NS_ENSURE_SUCCESS(rv, rv);
     929           1 :   rv = iconsFile->Append(DATABASE_FAVICONS_FILENAME);
     930           1 :   NS_ENSURE_SUCCESS(rv, rv);
     931           2 :   nsString iconsPath;
     932           1 :   rv = iconsFile->GetPath(iconsPath);
     933           1 :   NS_ENSURE_SUCCESS(rv, rv);
     934           2 :   rv = AttachDatabase(mMainConn, NS_ConvertUTF16toUTF8(iconsPath),
     935           3 :                       NS_LITERAL_CSTRING("favicons"));
     936           1 :   if (NS_FAILED(rv)) {
     937             :     // The favicons database may be corrupt. Try to replace and reattach it.
     938           0 :     rv = iconsFile->Remove(true);
     939           0 :     NS_ENSURE_SUCCESS(rv, rv);
     940           0 :     rv = EnsureFaviconsDatabaseFile(aStorage);
     941           0 :     NS_ENSURE_SUCCESS(rv, rv);
     942           0 :     rv = AttachDatabase(mMainConn, NS_ConvertUTF16toUTF8(iconsPath),
     943           0 :                         NS_LITERAL_CSTRING("favicons"));
     944           0 :     NS_ENSURE_SUCCESS(rv, rv);
     945             :   }
     946             : 
     947             :   // Create favicons temp entities.
     948           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_ICONS_AFTERINSERT_TRIGGER);
     949           1 :   NS_ENSURE_SUCCESS(rv, rv);
     950             : 
     951             :   // We use our functions during migration, so initialize them now.
     952           1 :   rv = InitFunctions();
     953           1 :   NS_ENSURE_SUCCESS(rv, rv);
     954             : 
     955           1 :   return NS_OK;
     956             : }
     957             : 
     958             : nsresult
     959           1 : Database::InitSchema(bool* aDatabaseMigrated)
     960             : {
     961           1 :   MOZ_ASSERT(NS_IsMainThread());
     962           1 :   *aDatabaseMigrated = false;
     963             : 
     964             :   // Get the database schema version.
     965             :   int32_t currentSchemaVersion;
     966           1 :   nsresult rv = mMainConn->GetSchemaVersion(&currentSchemaVersion);
     967           1 :   NS_ENSURE_SUCCESS(rv, rv);
     968           1 :   bool databaseInitialized = currentSchemaVersion > 0;
     969             : 
     970           1 :   if (databaseInitialized && currentSchemaVersion == DATABASE_SCHEMA_VERSION) {
     971             :     // The database is up to date and ready to go.
     972           1 :     return NS_OK;
     973             :   }
     974             : 
     975             :   // We are going to update the database, so everything from now on should be in
     976             :   // a transaction for performances.
     977           0 :   mozStorageTransaction transaction(mMainConn, false);
     978             : 
     979           0 :   if (databaseInitialized) {
     980             :     // Migration How-to:
     981             :     //
     982             :     // 1. increment PLACES_SCHEMA_VERSION.
     983             :     // 2. implement a method that performs upgrade to your version from the
     984             :     //    previous one.
     985             :     //
     986             :     // NOTE: The downgrade process is pretty much complicated by the fact old
     987             :     //       versions cannot know what a new version is going to implement.
     988             :     //       The only thing we will do for downgrades is setting back the schema
     989             :     //       version, so that next upgrades will run again the migration step.
     990             : 
     991           0 :     if (currentSchemaVersion < DATABASE_SCHEMA_VERSION) {
     992           0 :       *aDatabaseMigrated = true;
     993             : 
     994           0 :       if (currentSchemaVersion < 11) {
     995             :         // These are versions older than Firefox 4 that are not supported
     996             :         // anymore.  In this case it's safer to just replace the database.
     997           0 :         return NS_ERROR_FILE_CORRUPTED;
     998             :       }
     999             : 
    1000             :       // Firefox 4 uses schema version 11.
    1001             : 
    1002             :       // Firefox 8 uses schema version 12.
    1003             : 
    1004           0 :       if (currentSchemaVersion < 13) {
    1005           0 :         rv = MigrateV13Up();
    1006           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1007             :       }
    1008             : 
    1009           0 :       if (currentSchemaVersion < 15) {
    1010           0 :         rv = MigrateV15Up();
    1011           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1012             :       }
    1013             : 
    1014           0 :       if (currentSchemaVersion < 17) {
    1015           0 :         rv = MigrateV17Up();
    1016           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1017             :       }
    1018             : 
    1019             :       // Firefox 12 uses schema version 17.
    1020             : 
    1021           0 :       if (currentSchemaVersion < 18) {
    1022           0 :         rv = MigrateV18Up();
    1023           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1024             :       }
    1025             : 
    1026           0 :       if (currentSchemaVersion < 19) {
    1027           0 :         rv = MigrateV19Up();
    1028           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1029             :       }
    1030             : 
    1031             :       // Firefox 13 uses schema version 19.
    1032             : 
    1033           0 :       if (currentSchemaVersion < 20) {
    1034           0 :         rv = MigrateV20Up();
    1035           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1036             :       }
    1037             : 
    1038           0 :       if (currentSchemaVersion < 21) {
    1039           0 :         rv = MigrateV21Up();
    1040           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1041             :       }
    1042             : 
    1043             :       // Firefox 14 uses schema version 21.
    1044             : 
    1045           0 :       if (currentSchemaVersion < 22) {
    1046           0 :         rv = MigrateV22Up();
    1047           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1048             :       }
    1049             : 
    1050             :       // Firefox 22 uses schema version 22.
    1051             : 
    1052           0 :       if (currentSchemaVersion < 23) {
    1053           0 :         rv = MigrateV23Up();
    1054           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1055             :       }
    1056             : 
    1057             :       // Firefox 24 uses schema version 23.
    1058             : 
    1059           0 :       if (currentSchemaVersion < 24) {
    1060           0 :         rv = MigrateV24Up();
    1061           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1062             :       }
    1063             : 
    1064             :       // Firefox 34 uses schema version 24.
    1065             : 
    1066           0 :       if (currentSchemaVersion < 25) {
    1067           0 :         rv = MigrateV25Up();
    1068           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1069             :       }
    1070             : 
    1071             :       // Firefox 36 uses schema version 25.
    1072             : 
    1073           0 :       if (currentSchemaVersion < 26) {
    1074           0 :         rv = MigrateV26Up();
    1075           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1076             :       }
    1077             : 
    1078             :       // Firefox 37 uses schema version 26.
    1079             : 
    1080           0 :       if (currentSchemaVersion < 27) {
    1081           0 :         rv = MigrateV27Up();
    1082           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1083             :       }
    1084             : 
    1085           0 :       if (currentSchemaVersion < 28) {
    1086           0 :         rv = MigrateV28Up();
    1087           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1088             :       }
    1089             : 
    1090             :       // Firefox 39 uses schema version 28.
    1091             : 
    1092           0 :       if (currentSchemaVersion < 30) {
    1093           0 :         rv = MigrateV30Up();
    1094           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1095             :       }
    1096             : 
    1097             :       // Firefox 41 uses schema version 30.
    1098             : 
    1099           0 :       if (currentSchemaVersion < 31) {
    1100           0 :         rv = MigrateV31Up();
    1101           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1102             :       }
    1103             : 
    1104             :       // Firefox 48 uses schema version 31.
    1105             : 
    1106           0 :       if (currentSchemaVersion < 32) {
    1107           0 :         rv = MigrateV32Up();
    1108           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1109             :       }
    1110             : 
    1111             :       // Firefox 49 uses schema version 32.
    1112             : 
    1113           0 :       if (currentSchemaVersion < 33) {
    1114           0 :         rv = MigrateV33Up();
    1115           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1116             :       }
    1117             : 
    1118             :       // Firefox 50 uses schema version 33.
    1119             : 
    1120           0 :       if (currentSchemaVersion < 34) {
    1121           0 :         rv = MigrateV34Up();
    1122           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1123             :       }
    1124             : 
    1125             :       // Firefox 51 uses schema version 34.
    1126             : 
    1127           0 :       if (currentSchemaVersion < 35) {
    1128           0 :         rv = MigrateV35Up();
    1129           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1130             :       }
    1131             : 
    1132             :       // Firefox 52 uses schema version 35.
    1133             : 
    1134           0 :       if (currentSchemaVersion < 36) {
    1135           0 :         rv = MigrateV36Up();
    1136           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1137             :       }
    1138             : 
    1139           0 :       if (currentSchemaVersion < 37) {
    1140           0 :         rv = MigrateV37Up();
    1141           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1142             :       }
    1143             : 
    1144             :       // Firefox 55 uses schema version 37.
    1145             : 
    1146           0 :       if (currentSchemaVersion < 38) {
    1147           0 :         rv = MigrateV38Up();
    1148           0 :         NS_ENSURE_SUCCESS(rv, rv);
    1149             :       }
    1150             :       // Firefox 56 uses schema version 38.
    1151             : 
    1152             :       // Schema Upgrades must add migration code here.
    1153             : 
    1154           0 :       rv = UpdateBookmarkRootTitles();
    1155             :       // We don't want a broken localization to cause us to think
    1156             :       // the database is corrupt and needs to be replaced.
    1157           0 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    1158             :     }
    1159             :   }
    1160             :   else {
    1161             :     // This is a new database, so we have to create all the tables and indices.
    1162             : 
    1163             :     // moz_places.
    1164           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_PLACES);
    1165           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1166           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL_HASH);
    1167           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1168           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_REVHOST);
    1169           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1170           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_VISITCOUNT);
    1171           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1172           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_FRECENCY);
    1173           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1174           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_LASTVISITDATE);
    1175           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1176           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
    1177           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1178             : 
    1179             :     // moz_historyvisits.
    1180           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HISTORYVISITS);
    1181           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1182           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_PLACEDATE);
    1183           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1184           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_FROMVISIT);
    1185           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1186           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_HISTORYVISITS_VISITDATE);
    1187           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1188             : 
    1189             :     // moz_inputhistory.
    1190           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_INPUTHISTORY);
    1191           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1192             : 
    1193             :     // moz_hosts.
    1194           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS);
    1195           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1196             : 
    1197             :     // moz_bookmarks.
    1198           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS);
    1199           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1200           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_DELETED);
    1201           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1202           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACETYPE);
    1203           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1204           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PARENTPOSITION);
    1205           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1206           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
    1207           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1208           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
    1209           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1210             : 
    1211             :     // moz_keywords.
    1212           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_KEYWORDS);
    1213           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1214           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_KEYWORDS_PLACEPOSTDATA);
    1215           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1216             : 
    1217             :     // moz_anno_attributes.
    1218           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ANNO_ATTRIBUTES);
    1219           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1220             : 
    1221             :     // moz_annos.
    1222           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ANNOS);
    1223           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1224           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ANNOS_PLACEATTRIBUTE);
    1225           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1226             : 
    1227             :     // moz_items_annos.
    1228           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_ITEMS_ANNOS);
    1229           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1230           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE);
    1231           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1232             : 
    1233             :     // Initialize the bookmark roots in the new DB.
    1234           0 :     rv = CreateBookmarkRoots();
    1235           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1236             :   }
    1237             : 
    1238             :   // Set the schema version to the current one.
    1239           0 :   rv = mMainConn->SetSchemaVersion(DATABASE_SCHEMA_VERSION);
    1240           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1241             : 
    1242           0 :   rv = transaction.Commit();
    1243           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1244             : 
    1245             :   // ANY FAILURE IN THIS METHOD WILL CAUSE US TO MARK THE DATABASE AS CORRUPT
    1246             :   // AND TRY TO REPLACE IT.
    1247             :   // DO NOT PUT HERE ANYTHING THAT IS NOT RELATED TO INITIALIZATION OR MODIFYING
    1248             :   // THE DISK DATABASE.
    1249             : 
    1250           0 :   return NS_OK;
    1251             : }
    1252             : 
    1253             : nsresult
    1254           0 : Database::CreateBookmarkRoots()
    1255             : {
    1256           0 :   MOZ_ASSERT(NS_IsMainThread());
    1257             : 
    1258             :   nsCOMPtr<nsIStringBundleService> bundleService =
    1259           0 :     services::GetStringBundleService();
    1260           0 :   NS_ENSURE_STATE(bundleService);
    1261           0 :   nsCOMPtr<nsIStringBundle> bundle;
    1262           0 :   nsresult rv = bundleService->CreateBundle(PLACES_BUNDLE, getter_AddRefs(bundle));
    1263           0 :   if (NS_FAILED(rv)) return rv;
    1264             : 
    1265           0 :   nsXPIDLString rootTitle;
    1266             :   // The first root's title is an empty string.
    1267           0 :   rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("places"),
    1268           0 :                   NS_LITERAL_CSTRING("root________"), rootTitle);
    1269           0 :   if (NS_FAILED(rv)) return rv;
    1270             : 
    1271             :   // Fetch the internationalized folder name from the string bundle.
    1272           0 :   rv = bundle->GetStringFromName(u"BookmarksMenuFolderTitle",
    1273           0 :                                  getter_Copies(rootTitle));
    1274           0 :   if (NS_FAILED(rv)) return rv;
    1275           0 :   rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("menu"),
    1276           0 :                   NS_LITERAL_CSTRING("menu________"), rootTitle);
    1277           0 :   if (NS_FAILED(rv)) return rv;
    1278             : 
    1279           0 :   rv = bundle->GetStringFromName(u"BookmarksToolbarFolderTitle",
    1280           0 :                                  getter_Copies(rootTitle));
    1281           0 :   if (NS_FAILED(rv)) return rv;
    1282           0 :   rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("toolbar"),
    1283           0 :                   NS_LITERAL_CSTRING("toolbar_____"), rootTitle);
    1284           0 :   if (NS_FAILED(rv)) return rv;
    1285             : 
    1286           0 :   rv = bundle->GetStringFromName(u"TagsFolderTitle",
    1287           0 :                                  getter_Copies(rootTitle));
    1288           0 :   if (NS_FAILED(rv)) return rv;
    1289           0 :   rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("tags"),
    1290           0 :                   NS_LITERAL_CSTRING("tags________"), rootTitle);
    1291           0 :   if (NS_FAILED(rv)) return rv;
    1292             : 
    1293           0 :   rv = bundle->GetStringFromName(u"OtherBookmarksFolderTitle",
    1294           0 :                                  getter_Copies(rootTitle));
    1295           0 :   if (NS_FAILED(rv)) return rv;
    1296           0 :   rv = CreateRoot(mMainConn, NS_LITERAL_CSTRING("unfiled"),
    1297           0 :                   NS_LITERAL_CSTRING("unfiled_____"), rootTitle);
    1298           0 :   if (NS_FAILED(rv)) return rv;
    1299             : 
    1300           0 :   int64_t mobileRootId = CreateMobileRoot();
    1301           0 :   if (mobileRootId <= 0) return NS_ERROR_FAILURE;
    1302             :   {
    1303           0 :     nsCOMPtr<mozIStorageStatement> mobileRootSyncStatusStmt;
    1304           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1305             :       "UPDATE moz_bookmarks SET syncStatus = :sync_status WHERE id = :id"
    1306           0 :     ), getter_AddRefs(mobileRootSyncStatusStmt));
    1307           0 :     if (NS_FAILED(rv)) return rv;
    1308             :     mozStorageStatementScoper mobileRootSyncStatusScoper(
    1309           0 :       mobileRootSyncStatusStmt);
    1310             : 
    1311           0 :     rv = mobileRootSyncStatusStmt->BindInt32ByName(
    1312           0 :       NS_LITERAL_CSTRING("sync_status"),
    1313             :       nsINavBookmarksService::SYNC_STATUS_NEW
    1314           0 :     );
    1315           0 :     if (NS_FAILED(rv)) return rv;
    1316           0 :     rv = mobileRootSyncStatusStmt->BindInt64ByName(NS_LITERAL_CSTRING("id"),
    1317           0 :                                                    mobileRootId);
    1318           0 :     if (NS_FAILED(rv)) return rv;
    1319             : 
    1320           0 :     rv = mobileRootSyncStatusStmt->Execute();
    1321           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1322             :   }
    1323             : 
    1324             : #if DEBUG
    1325           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1326           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1327             :     "SELECT count(*), sum(position) FROM moz_bookmarks"
    1328           0 :   ), getter_AddRefs(stmt));
    1329           0 :   if (NS_FAILED(rv)) return rv;
    1330             : 
    1331             :   bool hasResult;
    1332           0 :   rv = stmt->ExecuteStep(&hasResult);
    1333           0 :   if (NS_FAILED(rv)) return rv;
    1334           0 :   MOZ_ASSERT(hasResult);
    1335           0 :   int32_t bookmarkCount = stmt->AsInt32(0);
    1336           0 :   int32_t positionSum = stmt->AsInt32(1);
    1337           0 :   MOZ_ASSERT(bookmarkCount == 6 && positionSum == 10);
    1338             : #endif
    1339             : 
    1340           0 :   return NS_OK;
    1341             : }
    1342             : 
    1343             : nsresult
    1344           1 : Database::InitFunctions()
    1345             : {
    1346           1 :   MOZ_ASSERT(NS_IsMainThread());
    1347             : 
    1348           1 :   nsresult rv = GetUnreversedHostFunction::create(mMainConn);
    1349           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1350           1 :   rv = MatchAutoCompleteFunction::create(mMainConn);
    1351           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1352           1 :   rv = CalculateFrecencyFunction::create(mMainConn);
    1353           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1354           1 :   rv = GenerateGUIDFunction::create(mMainConn);
    1355           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1356           1 :   rv = FixupURLFunction::create(mMainConn);
    1357           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1358           1 :   rv = FrecencyNotificationFunction::create(mMainConn);
    1359           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1360           1 :   rv = StoreLastInsertedIdFunction::create(mMainConn);
    1361           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1362           1 :   rv = HashFunction::create(mMainConn);
    1363           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1364             : 
    1365           1 :   return NS_OK;
    1366             : }
    1367             : 
    1368             : nsresult
    1369           1 : Database::InitTempEntities()
    1370             : {
    1371           1 :   MOZ_ASSERT(NS_IsMainThread());
    1372             : 
    1373           1 :   nsresult rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERINSERT_TRIGGER);
    1374           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1375           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_HISTORYVISITS_AFTERDELETE_TRIGGER);
    1376           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1377             : 
    1378             :   // Add the triggers that update the moz_hosts table as necessary.
    1379           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERINSERT_TRIGGER);
    1380           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1381           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_UPDATEHOSTS_TEMP);
    1382           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1383           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_UPDATEHOSTS_AFTERDELETE_TRIGGER);
    1384           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1385           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERDELETE_TRIGGER);
    1386           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1387           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_FRECENCY_TRIGGER);
    1388           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1389           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_PLACES_AFTERUPDATE_TYPED_TRIGGER);
    1390           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1391             : 
    1392           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_BOOKMARKS_FOREIGNCOUNT_AFTERDELETE_TRIGGER);
    1393           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1394           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_BOOKMARKS_FOREIGNCOUNT_AFTERINSERT_TRIGGER);
    1395           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1396           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_BOOKMARKS_FOREIGNCOUNT_AFTERUPDATE_TRIGGER);
    1397           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1398             : 
    1399           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_KEYWORDS_FOREIGNCOUNT_AFTERDELETE_TRIGGER);
    1400           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1401           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_KEYWORDS_FOREIGNCOUNT_AFTERINSERT_TRIGGER);
    1402           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1403           1 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_KEYWORDS_FOREIGNCOUNT_AFTERUPDATE_TRIGGER);
    1404           1 :   NS_ENSURE_SUCCESS(rv, rv);
    1405             : 
    1406           1 :   return NS_OK;
    1407             : }
    1408             : 
    1409             : nsresult
    1410           0 : Database::UpdateBookmarkRootTitles()
    1411             : {
    1412           0 :   MOZ_ASSERT(NS_IsMainThread());
    1413             : 
    1414             :   nsCOMPtr<nsIStringBundleService> bundleService =
    1415           0 :     services::GetStringBundleService();
    1416           0 :   NS_ENSURE_STATE(bundleService);
    1417             : 
    1418           0 :   nsCOMPtr<nsIStringBundle> bundle;
    1419           0 :   nsresult rv = bundleService->CreateBundle(PLACES_BUNDLE, getter_AddRefs(bundle));
    1420           0 :   if (NS_FAILED(rv)) return rv;
    1421             : 
    1422           0 :   nsCOMPtr<mozIStorageAsyncStatement> stmt;
    1423           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1424             :     "UPDATE moz_bookmarks SET title = :new_title WHERE guid = :guid"
    1425           0 :   ), getter_AddRefs(stmt));
    1426           0 :   if (NS_FAILED(rv)) return rv;
    1427             : 
    1428           0 :   nsCOMPtr<mozIStorageBindingParamsArray> paramsArray;
    1429           0 :   rv = stmt->NewBindingParamsArray(getter_AddRefs(paramsArray));
    1430           0 :   if (NS_FAILED(rv)) return rv;
    1431             : 
    1432             :   const char *rootGuids[] = { "menu________"
    1433             :                             , "toolbar_____"
    1434             :                             , "tags________"
    1435             :                             , "unfiled_____"
    1436             :                             , "mobile______"
    1437           0 :                             };
    1438             :   const char *titleStringIDs[] = { "BookmarksMenuFolderTitle"
    1439             :                                  , "BookmarksToolbarFolderTitle"
    1440             :                                  , "TagsFolderTitle"
    1441             :                                  , "OtherBookmarksFolderTitle"
    1442             :                                  , "MobileBookmarksFolderTitle"
    1443           0 :                                  };
    1444             : 
    1445           0 :   for (uint32_t i = 0; i < ArrayLength(rootGuids); ++i) {
    1446           0 :     nsXPIDLString title;
    1447           0 :     rv = bundle->GetStringFromName(NS_ConvertASCIItoUTF16(titleStringIDs[i]).get(),
    1448           0 :                                    getter_Copies(title));
    1449           0 :     if (NS_FAILED(rv)) return rv;
    1450             : 
    1451           0 :     nsCOMPtr<mozIStorageBindingParams> params;
    1452           0 :     rv = paramsArray->NewBindingParams(getter_AddRefs(params));
    1453           0 :     if (NS_FAILED(rv)) return rv;
    1454           0 :     rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
    1455           0 :                                       nsDependentCString(rootGuids[i]));
    1456           0 :     if (NS_FAILED(rv)) return rv;
    1457           0 :     rv = params->BindUTF8StringByName(NS_LITERAL_CSTRING("new_title"),
    1458           0 :                                       NS_ConvertUTF16toUTF8(title));
    1459           0 :     if (NS_FAILED(rv)) return rv;
    1460           0 :     rv = paramsArray->AddParams(params);
    1461           0 :     if (NS_FAILED(rv)) return rv;
    1462             :   }
    1463             : 
    1464           0 :   rv = stmt->BindParameters(paramsArray);
    1465           0 :   if (NS_FAILED(rv)) return rv;
    1466           0 :   nsCOMPtr<mozIStoragePendingStatement> pendingStmt;
    1467           0 :   rv = stmt->ExecuteAsync(nullptr, getter_AddRefs(pendingStmt));
    1468           0 :   if (NS_FAILED(rv)) return rv;
    1469             : 
    1470           0 :   return NS_OK;
    1471             : }
    1472             : 
    1473             : nsresult
    1474           0 : Database::MigrateV13Up()
    1475             : {
    1476           0 :   MOZ_ASSERT(NS_IsMainThread());
    1477             : 
    1478             :   // Dynamic containers are no longer supported.
    1479           0 :   nsCOMPtr<mozIStorageAsyncStatement> deleteDynContainersStmt;
    1480           0 :   nsresult rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1481             :       "DELETE FROM moz_bookmarks WHERE type = :item_type"),
    1482           0 :     getter_AddRefs(deleteDynContainersStmt));
    1483           0 :   rv = deleteDynContainersStmt->BindInt32ByName(
    1484           0 :     NS_LITERAL_CSTRING("item_type"),
    1485             :     nsINavBookmarksService::TYPE_DYNAMIC_CONTAINER
    1486           0 :   );
    1487           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1488           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    1489           0 :   rv = deleteDynContainersStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
    1490           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1491             : 
    1492           0 :   return NS_OK;
    1493             : }
    1494             : 
    1495             : nsresult
    1496           0 : Database::MigrateV15Up()
    1497             : {
    1498           0 :   MOZ_ASSERT(NS_IsMainThread());
    1499             : 
    1500             :   // Drop moz_bookmarks_beforedelete_v1_trigger, since it's more expensive than
    1501             :   // useful.
    1502           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1503             :     "DROP TRIGGER IF EXISTS moz_bookmarks_beforedelete_v1_trigger"
    1504           0 :   ));
    1505           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1506             : 
    1507             :   // Remove any orphan keywords.
    1508           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1509             :     "DELETE FROM moz_keywords "
    1510             :     "WHERE NOT EXISTS ( "
    1511             :       "SELECT id "
    1512             :       "FROM moz_bookmarks "
    1513             :       "WHERE keyword_id = moz_keywords.id "
    1514             :     ")"
    1515           0 :   ));
    1516           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1517             : 
    1518           0 :   return NS_OK;
    1519             : }
    1520             : 
    1521             : nsresult
    1522           0 : Database::MigrateV17Up()
    1523             : {
    1524           0 :   MOZ_ASSERT(NS_IsMainThread());
    1525             : 
    1526           0 :   bool tableExists = false;
    1527             : 
    1528           0 :   nsresult rv = mMainConn->TableExists(NS_LITERAL_CSTRING("moz_hosts"), &tableExists);
    1529           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1530             : 
    1531           0 :   if (!tableExists) {
    1532             :     // For anyone who used in-development versions of this autocomplete,
    1533             :     // drop the old tables and its indexes.
    1534           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1535             :       "DROP INDEX IF EXISTS moz_hostnames_frecencyindex"
    1536           0 :     ));
    1537           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1538           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1539             :       "DROP TABLE IF EXISTS moz_hostnames"
    1540           0 :     ));
    1541           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1542             : 
    1543             :     // Add the moz_hosts table so we can get hostnames for URL autocomplete.
    1544           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_HOSTS);
    1545           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1546             :   }
    1547             : 
    1548             :   // Fill the moz_hosts table with all the domains in moz_places.
    1549           0 :   nsCOMPtr<mozIStorageAsyncStatement> fillHostsStmt;
    1550           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1551             :     "INSERT OR IGNORE INTO moz_hosts (host, frecency) "
    1552             :         "SELECT fixup_url(get_unreversed_host(h.rev_host)) AS host, "
    1553             :                "(SELECT MAX(frecency) FROM moz_places "
    1554             :                 "WHERE rev_host = h.rev_host "
    1555             :                    "OR rev_host = h.rev_host || 'www.' "
    1556             :                ") AS frecency "
    1557             :         "FROM moz_places h "
    1558             :         "WHERE LENGTH(h.rev_host) > 1 "
    1559             :         "GROUP BY h.rev_host"
    1560           0 :   ), getter_AddRefs(fillHostsStmt));
    1561           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1562             : 
    1563           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    1564           0 :   rv = fillHostsStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
    1565           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1566             : 
    1567           0 :   return NS_OK;
    1568             : }
    1569             : 
    1570             : nsresult
    1571           0 : Database::MigrateV18Up()
    1572             : {
    1573           0 :   MOZ_ASSERT(NS_IsMainThread());
    1574             : 
    1575             :   // moz_hosts should distinguish on typed entries.
    1576             : 
    1577             :   // Check if the profile already has a typed column.
    1578           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1579           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1580             :     "SELECT typed FROM moz_hosts"
    1581           0 :   ), getter_AddRefs(stmt));
    1582           0 :   if (NS_FAILED(rv)) {
    1583           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1584             :       "ALTER TABLE moz_hosts ADD COLUMN typed NOT NULL DEFAULT 0"
    1585           0 :     ));
    1586           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1587             :   }
    1588             : 
    1589             :   // With the addition of the typed column the covering index loses its
    1590             :   // advantages.  On the other side querying on host and (optionally) typed
    1591             :   // largely restricts the number of results, making scans decently fast.
    1592           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1593             :     "DROP INDEX IF EXISTS moz_hosts_frecencyhostindex"
    1594           0 :   ));
    1595           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1596             : 
    1597             :   // Update typed data.
    1598           0 :   nsCOMPtr<mozIStorageAsyncStatement> updateTypedStmt;
    1599           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1600             :     "UPDATE moz_hosts SET typed = 1 WHERE host IN ( "
    1601             :       "SELECT fixup_url(get_unreversed_host(rev_host)) "
    1602             :       "FROM moz_places WHERE typed = 1 "
    1603             :     ") "
    1604           0 :   ), getter_AddRefs(updateTypedStmt));
    1605           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1606             : 
    1607           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    1608           0 :   rv = updateTypedStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
    1609           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1610             : 
    1611           0 :   return NS_OK;
    1612             : }
    1613             : 
    1614             : nsresult
    1615           0 : Database::MigrateV19Up()
    1616             : {
    1617           0 :   MOZ_ASSERT(NS_IsMainThread());
    1618             : 
    1619             :   // Livemarks children are no longer bookmarks.
    1620             : 
    1621             :   // Remove all children of folders annotated as livemarks.
    1622           0 :   nsCOMPtr<mozIStorageStatement> deleteLivemarksChildrenStmt;
    1623           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1624             :     "DELETE FROM moz_bookmarks WHERE parent IN("
    1625             :       "SELECT b.id FROM moz_bookmarks b "
    1626             :       "JOIN moz_items_annos a ON a.item_id = b.id "
    1627             :       "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
    1628             :       "WHERE b.type = :item_type AND n.name = :anno_name "
    1629             :     ")"
    1630           0 :   ), getter_AddRefs(deleteLivemarksChildrenStmt));
    1631           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1632           0 :   rv = deleteLivemarksChildrenStmt->BindUTF8StringByName(
    1633           0 :     NS_LITERAL_CSTRING("anno_name"), NS_LITERAL_CSTRING(LMANNO_FEEDURI)
    1634           0 :   );
    1635           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1636           0 :   rv = deleteLivemarksChildrenStmt->BindInt32ByName(
    1637           0 :     NS_LITERAL_CSTRING("item_type"), nsINavBookmarksService::TYPE_FOLDER
    1638           0 :   );
    1639           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1640           0 :   rv = deleteLivemarksChildrenStmt->Execute();
    1641           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1642             : 
    1643             :   // Clear obsolete livemark prefs.
    1644           0 :   (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_seconds");
    1645           0 :   (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_limit_count");
    1646           0 :   (void)Preferences::ClearUser("browser.bookmarks.livemark_refresh_delay_time");
    1647             : 
    1648             :   // Remove the old status annotations.
    1649           0 :   nsCOMPtr<mozIStorageStatement> deleteLivemarksAnnosStmt;
    1650           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1651             :     "DELETE FROM moz_items_annos WHERE anno_attribute_id IN("
    1652             :       "SELECT id FROM moz_anno_attributes "
    1653             :       "WHERE name IN (:anno_loading, :anno_loadfailed, :anno_expiration) "
    1654             :     ")"
    1655           0 :   ), getter_AddRefs(deleteLivemarksAnnosStmt));
    1656           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1657           0 :   rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
    1658           0 :     NS_LITERAL_CSTRING("anno_loading"), NS_LITERAL_CSTRING("livemark/loading")
    1659           0 :   );
    1660           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1661           0 :   rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
    1662           0 :     NS_LITERAL_CSTRING("anno_loadfailed"), NS_LITERAL_CSTRING("livemark/loadfailed")
    1663           0 :   );
    1664           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1665           0 :   rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
    1666           0 :     NS_LITERAL_CSTRING("anno_expiration"), NS_LITERAL_CSTRING("livemark/expiration")
    1667           0 :   );
    1668           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1669           0 :   rv = deleteLivemarksAnnosStmt->Execute();
    1670           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1671             : 
    1672             :   // Remove orphan annotation names.
    1673           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1674             :     "DELETE FROM moz_anno_attributes "
    1675             :       "WHERE name IN (:anno_loading, :anno_loadfailed, :anno_expiration) "
    1676           0 :   ), getter_AddRefs(deleteLivemarksAnnosStmt));
    1677           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1678           0 :   rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
    1679           0 :     NS_LITERAL_CSTRING("anno_loading"), NS_LITERAL_CSTRING("livemark/loading")
    1680           0 :   );
    1681           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1682           0 :   rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
    1683           0 :     NS_LITERAL_CSTRING("anno_loadfailed"), NS_LITERAL_CSTRING("livemark/loadfailed")
    1684           0 :   );
    1685           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1686           0 :   rv = deleteLivemarksAnnosStmt->BindUTF8StringByName(
    1687           0 :     NS_LITERAL_CSTRING("anno_expiration"), NS_LITERAL_CSTRING("livemark/expiration")
    1688           0 :   );
    1689           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1690           0 :   rv = deleteLivemarksAnnosStmt->Execute();
    1691           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1692             : 
    1693           0 :   return NS_OK;
    1694             : }
    1695             : 
    1696             : nsresult
    1697           0 : Database::MigrateV20Up()
    1698             : {
    1699           0 :   MOZ_ASSERT(NS_IsMainThread());
    1700             : 
    1701             :   // Remove obsolete bookmark GUID annotations.
    1702           0 :   nsCOMPtr<mozIStorageStatement> deleteOldBookmarkGUIDAnnosStmt;
    1703           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1704             :     "DELETE FROM moz_items_annos WHERE anno_attribute_id = ("
    1705             :       "SELECT id FROM moz_anno_attributes "
    1706             :       "WHERE name = :anno_guid"
    1707             :     ")"
    1708           0 :   ), getter_AddRefs(deleteOldBookmarkGUIDAnnosStmt));
    1709           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1710           0 :   rv = deleteOldBookmarkGUIDAnnosStmt->BindUTF8StringByName(
    1711           0 :     NS_LITERAL_CSTRING("anno_guid"), NS_LITERAL_CSTRING("placesInternal/GUID")
    1712           0 :   );
    1713           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1714           0 :   rv = deleteOldBookmarkGUIDAnnosStmt->Execute();
    1715           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1716             : 
    1717             :   // Remove the orphan annotation name.
    1718           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1719             :     "DELETE FROM moz_anno_attributes "
    1720             :       "WHERE name = :anno_guid"
    1721           0 :   ), getter_AddRefs(deleteOldBookmarkGUIDAnnosStmt));
    1722           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1723           0 :   rv = deleteOldBookmarkGUIDAnnosStmt->BindUTF8StringByName(
    1724           0 :     NS_LITERAL_CSTRING("anno_guid"), NS_LITERAL_CSTRING("placesInternal/GUID")
    1725           0 :   );
    1726           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1727           0 :   rv = deleteOldBookmarkGUIDAnnosStmt->Execute();
    1728           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1729             : 
    1730           0 :   return NS_OK;
    1731             : }
    1732             : 
    1733             : nsresult
    1734           0 : Database::MigrateV21Up()
    1735             : {
    1736           0 :   MOZ_ASSERT(NS_IsMainThread());
    1737             : 
    1738             :   // Add a prefix column to moz_hosts.
    1739           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1740           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1741             :     "SELECT prefix FROM moz_hosts"
    1742           0 :   ), getter_AddRefs(stmt));
    1743           0 :   if (NS_FAILED(rv)) {
    1744           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1745             :       "ALTER TABLE moz_hosts ADD COLUMN prefix"
    1746           0 :     ));
    1747           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1748             :   }
    1749             : 
    1750           0 :   return NS_OK;
    1751             : }
    1752             : 
    1753             : nsresult
    1754           0 : Database::MigrateV22Up()
    1755             : {
    1756           0 :   MOZ_ASSERT(NS_IsMainThread());
    1757             : 
    1758             :   // Reset all session IDs to 0 since we don't support them anymore.
    1759             :   // We don't set them to NULL to avoid breaking downgrades.
    1760           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1761           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1762             :     "SELECT session FROM moz_historyvisits"
    1763           0 :   ), getter_AddRefs(stmt));
    1764           0 :   if (NS_SUCCEEDED(rv)) {
    1765           0 :     nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1766             :       "UPDATE moz_historyvisits SET session = 0"
    1767           0 :     ));
    1768           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1769             :   }
    1770             : 
    1771           0 :   return NS_OK;
    1772             : }
    1773             : 
    1774             : 
    1775             : nsresult
    1776           0 : Database::MigrateV23Up()
    1777             : {
    1778           0 :   MOZ_ASSERT(NS_IsMainThread());
    1779             : 
    1780             :   // Recalculate hosts prefixes.
    1781           0 :   nsCOMPtr<mozIStorageAsyncStatement> updatePrefixesStmt;
    1782           0 :   nsresult rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    1783             :     "UPDATE moz_hosts SET prefix = ( " HOSTS_PREFIX_PRIORITY_FRAGMENT ") "
    1784           0 :   ), getter_AddRefs(updatePrefixesStmt));
    1785           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1786             : 
    1787           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    1788           0 :   rv = updatePrefixesStmt->ExecuteAsync(nullptr, getter_AddRefs(ps));
    1789           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1790             : 
    1791           0 :   return NS_OK;
    1792             : }
    1793             : 
    1794             : nsresult
    1795           0 : Database::MigrateV24Up()
    1796             : {
    1797           0 :   MOZ_ASSERT(NS_IsMainThread());
    1798             : 
    1799             :  // Add a foreign_count column to moz_places
    1800           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1801           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1802             :     "SELECT foreign_count FROM moz_places"
    1803           0 :   ), getter_AddRefs(stmt));
    1804           0 :   if (NS_FAILED(rv)) {
    1805           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1806           0 :       "ALTER TABLE moz_places ADD COLUMN foreign_count INTEGER DEFAULT 0 NOT NULL"));
    1807           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1808             :   }
    1809             : 
    1810             :   // Adjust counts for all the rows
    1811           0 :   nsCOMPtr<mozIStorageStatement> updateStmt;
    1812           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1813             :     "UPDATE moz_places SET foreign_count = "
    1814             :     "(SELECT count(*) FROM moz_bookmarks WHERE fk = moz_places.id) "
    1815           0 :   ), getter_AddRefs(updateStmt));
    1816           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1817           0 :   mozStorageStatementScoper updateScoper(updateStmt);
    1818           0 :   rv = updateStmt->Execute();
    1819           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1820             : 
    1821           0 :   return NS_OK;
    1822             : }
    1823             : 
    1824             : nsresult
    1825           0 : Database::MigrateV25Up()
    1826             : {
    1827           0 :   MOZ_ASSERT(NS_IsMainThread());
    1828             : 
    1829             :   // Change bookmark roots GUIDs to constant values.
    1830             : 
    1831             :   // If moz_bookmarks_roots doesn't exist anymore, it's because we finally have
    1832             :   // been able to remove it.  In such a case, we already assigned constant GUIDs
    1833             :   // to the roots and we can skip this migration.
    1834             :   {
    1835           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    1836           0 :     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1837             :       "SELECT root_name FROM moz_bookmarks_roots"
    1838           0 :     ), getter_AddRefs(stmt));
    1839           0 :     if (NS_FAILED(rv)) {
    1840           0 :       return NS_OK;
    1841             :     }
    1842             :   }
    1843             : 
    1844           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1845           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1846             :     "UPDATE moz_bookmarks SET guid = :guid "
    1847             :     "WHERE id = (SELECT folder_id FROM moz_bookmarks_roots WHERE root_name = :name) "
    1848           0 :   ), getter_AddRefs(stmt));
    1849           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1850             : 
    1851           0 :   const char *rootNames[] = { "places", "menu", "toolbar", "tags", "unfiled" };
    1852             :   const char *rootGuids[] = { "root________"
    1853             :                             , "menu________"
    1854             :                             , "toolbar_____"
    1855             :                             , "tags________"
    1856             :                             , "unfiled_____"
    1857           0 :                             };
    1858             : 
    1859           0 :   for (uint32_t i = 0; i < ArrayLength(rootNames); ++i) {
    1860             :     // Since this is using the synchronous API, we cannot use
    1861             :     // a BindingParamsArray.
    1862           0 :     mozStorageStatementScoper scoper(stmt);
    1863             : 
    1864           0 :     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("name"),
    1865           0 :                                       nsDependentCString(rootNames[i]));
    1866           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1867           0 :     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
    1868           0 :                                       nsDependentCString(rootGuids[i]));
    1869           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1870             : 
    1871           0 :     rv = stmt->Execute();
    1872           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1873             :   }
    1874             : 
    1875           0 :   return NS_OK;
    1876             : }
    1877             : 
    1878             : nsresult
    1879           0 : Database::MigrateV26Up() {
    1880           0 :   MOZ_ASSERT(NS_IsMainThread());
    1881             : 
    1882             :   // Round down dateAdded and lastModified values to milliseconds precision.
    1883           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1884             :     "UPDATE moz_bookmarks SET dateAdded = dateAdded - dateAdded % 1000, "
    1885           0 :     "                         lastModified = lastModified - lastModified % 1000"));
    1886           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1887             : 
    1888           0 :   return NS_OK;
    1889             : }
    1890             : 
    1891             : nsresult
    1892           0 : Database::MigrateV27Up() {
    1893           0 :   MOZ_ASSERT(NS_IsMainThread());
    1894             : 
    1895             :   // Change keywords store, moving their relation from bookmarks to urls.
    1896           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    1897           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    1898             :     "SELECT place_id FROM moz_keywords"
    1899           0 :   ), getter_AddRefs(stmt));
    1900           0 :   if (NS_FAILED(rv)) {
    1901             :     // Even if these 2 columns have a unique constraint, we allow NULL values
    1902             :     // for backwards compatibility. NULL never breaks a unique constraint.
    1903           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1904           0 :       "ALTER TABLE moz_keywords ADD COLUMN place_id INTEGER"));
    1905           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1906           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1907           0 :       "ALTER TABLE moz_keywords ADD COLUMN post_data TEXT"));
    1908           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1909           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_KEYWORDS_PLACEPOSTDATA);
    1910           0 :     NS_ENSURE_SUCCESS(rv, rv);
    1911             :   }
    1912             : 
    1913             :   // Associate keywords with uris.  A keyword could be associated to multiple
    1914             :   // bookmarks uris, or multiple keywords could be associated to the same uri.
    1915             :   // The new system only allows multiple uris per keyword, provided they have
    1916             :   // a different post_data value.
    1917           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1918             :     "INSERT OR REPLACE INTO moz_keywords (id, keyword, place_id, post_data) "
    1919             :     "SELECT k.id, k.keyword, h.id, MAX(a.content) "
    1920             :     "FROM moz_places h "
    1921             :     "JOIN moz_bookmarks b ON b.fk = h.id "
    1922             :     "JOIN moz_keywords k ON k.id = b.keyword_id "
    1923             :     "LEFT JOIN moz_items_annos a ON a.item_id = b.id "
    1924             :                                "AND a.anno_attribute_id = (SELECT id FROM moz_anno_attributes "
    1925             :                                                           "WHERE name = 'bookmarkProperties/POSTData') "
    1926             :     "WHERE k.place_id ISNULL "
    1927           0 :     "GROUP BY keyword"));
    1928           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1929             : 
    1930             :   // Remove any keyword that points to a non-existing place id.
    1931           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1932             :     "DELETE FROM moz_keywords "
    1933           0 :     "WHERE NOT EXISTS (SELECT 1 FROM moz_places WHERE id = moz_keywords.place_id)"));
    1934           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1935           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1936             :     "UPDATE moz_bookmarks SET keyword_id = NULL "
    1937           0 :     "WHERE NOT EXISTS (SELECT 1 FROM moz_keywords WHERE id = moz_bookmarks.keyword_id)"));
    1938           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1939             : 
    1940             :   // Adjust foreign_count for all the rows.
    1941           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1942             :     "UPDATE moz_places SET foreign_count = "
    1943             :     "(SELECT count(*) FROM moz_bookmarks WHERE fk = moz_places.id) + "
    1944             :     "(SELECT count(*) FROM moz_keywords WHERE place_id = moz_places.id) "
    1945           0 :   ));
    1946           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1947             : 
    1948           0 :   return NS_OK;
    1949             : }
    1950             : 
    1951             : nsresult
    1952           0 : Database::MigrateV28Up() {
    1953           0 :   MOZ_ASSERT(NS_IsMainThread());
    1954             : 
    1955             :   // v27 migration was bogus and set some unrelated annotations as post_data for
    1956             :   // keywords having an annotated bookmark.
    1957             :   // The current v27 migration function is fixed, but we still need to handle
    1958             :   // users that hit the bogus version.  Since we can't distinguish, we'll just
    1959             :   // set again all of the post data.
    1960           0 :   DebugOnly<nsresult> rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1961             :     "UPDATE moz_keywords "
    1962             :     "SET post_data = ( "
    1963             :       "SELECT content FROM moz_items_annos a "
    1964             :       "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
    1965             :       "JOIN moz_bookmarks b on b.id = a.item_id "
    1966             :       "WHERE n.name = 'bookmarkProperties/POSTData' "
    1967             :       "AND b.keyword_id = moz_keywords.id "
    1968             :       "ORDER BY b.lastModified DESC "
    1969             :       "LIMIT 1 "
    1970             :     ") "
    1971             :     "WHERE EXISTS(SELECT 1 FROM moz_bookmarks WHERE keyword_id = moz_keywords.id) "
    1972           0 :   ));
    1973             :   // In case the update fails a constraint, we don't want to throw away the
    1974             :   // whole database for just a few keywords.  In rare cases the user might have
    1975             :   // to recreate them.  Though, at this point, there shouldn't be 2 keywords
    1976             :   // pointing to the same url and post data, cause the previous migration step
    1977             :   // removed them.
    1978           0 :   MOZ_ASSERT(NS_SUCCEEDED(rv));
    1979             : 
    1980           0 :   return NS_OK;
    1981             : }
    1982             : 
    1983             : nsresult
    1984           0 : Database::MigrateV30Up() {
    1985           0 :   MOZ_ASSERT(NS_IsMainThread());
    1986             : 
    1987           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    1988             :     "DROP INDEX IF EXISTS moz_favicons_guid_uniqueindex"
    1989           0 :   ));
    1990           0 :   NS_ENSURE_SUCCESS(rv, rv);
    1991             : 
    1992           0 :   return NS_OK;
    1993             : }
    1994             : 
    1995             : nsresult
    1996           0 : Database::MigrateV31Up() {
    1997           0 :   MOZ_ASSERT(NS_IsMainThread());
    1998             : 
    1999           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2000             :     "DROP TABLE IF EXISTS moz_bookmarks_roots"
    2001           0 :   ));
    2002           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2003             : 
    2004           0 :   return NS_OK;
    2005             : }
    2006             : 
    2007             : nsresult
    2008           0 : Database::MigrateV32Up() {
    2009           0 :   MOZ_ASSERT(NS_IsMainThread());
    2010             : 
    2011             :   // Remove some old and no more used Places preferences that may be confusing
    2012             :   // for the user.
    2013           0 :   mozilla::Unused << Preferences::ClearUser("places.history.expiration.transient_optimal_database_size");
    2014           0 :   mozilla::Unused << Preferences::ClearUser("places.last_vacuum");
    2015           0 :   mozilla::Unused << Preferences::ClearUser("browser.history_expire_sites");
    2016           0 :   mozilla::Unused << Preferences::ClearUser("browser.history_expire_days.mirror");
    2017           0 :   mozilla::Unused << Preferences::ClearUser("browser.history_expire_days_min");
    2018             : 
    2019             :   // For performance reasons we want to remove too long urls from history.
    2020             :   // We cannot use the moz_places triggers here, cause they are defined only
    2021             :   // after the schema migration.  Thus we need to collect the hosts that need to
    2022             :   // be updated first.
    2023           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2024             :     "CREATE TEMP TABLE moz_migrate_v32_temp ("
    2025             :       "host TEXT PRIMARY KEY "
    2026             :     ") WITHOUT ROWID "
    2027           0 :   ));
    2028           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2029             :   {
    2030           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    2031           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2032             :       "INSERT OR IGNORE INTO moz_migrate_v32_temp (host) "
    2033             :         "SELECT fixup_url(get_unreversed_host(rev_host)) "
    2034             :         "FROM moz_places WHERE LENGTH(url) > :maxlen AND foreign_count = 0"
    2035           0 :     ), getter_AddRefs(stmt));
    2036           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2037           0 :     mozStorageStatementScoper scoper(stmt);
    2038           0 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("maxlen"), MaxUrlLength());
    2039           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2040           0 :     rv = stmt->Execute();
    2041           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2042             :   }
    2043             :   // Now remove the pages with a long url.
    2044             :   {
    2045           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    2046           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2047             :       "DELETE FROM moz_places WHERE LENGTH(url) > :maxlen AND foreign_count = 0"
    2048           0 :     ), getter_AddRefs(stmt));
    2049           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2050           0 :     mozStorageStatementScoper scoper(stmt);
    2051           0 :     rv = stmt->BindInt32ByName(NS_LITERAL_CSTRING("maxlen"), MaxUrlLength());
    2052           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2053           0 :     rv = stmt->Execute();
    2054           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2055             :   }
    2056             : 
    2057             :   // Expire orphan visits and update moz_hosts.
    2058             :   // These may be a bit more expensive and are not critical for the DB
    2059             :   // functionality, so we execute them asynchronously.
    2060           0 :   nsCOMPtr<mozIStorageAsyncStatement> expireOrphansStmt;
    2061           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    2062             :     "DELETE FROM moz_historyvisits "
    2063             :     "WHERE NOT EXISTS (SELECT 1 FROM moz_places WHERE id = place_id)"
    2064           0 :   ), getter_AddRefs(expireOrphansStmt));
    2065           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2066           0 :   nsCOMPtr<mozIStorageAsyncStatement> deleteHostsStmt;
    2067           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    2068             :     "DELETE FROM moz_hosts "
    2069             :     "WHERE host IN (SELECT host FROM moz_migrate_v32_temp) "
    2070             :       "AND NOT EXISTS("
    2071             :         "SELECT 1 FROM moz_places "
    2072             :           "WHERE rev_host = get_unreversed_host(host || '.') || '.' "
    2073             :              "OR rev_host = get_unreversed_host(host || '.') || '.www.' "
    2074             :       "); "
    2075           0 :   ), getter_AddRefs(deleteHostsStmt));
    2076           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2077           0 :   nsCOMPtr<mozIStorageAsyncStatement> updateHostsStmt;
    2078           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    2079             :     "UPDATE moz_hosts "
    2080             :     "SET prefix = (" HOSTS_PREFIX_PRIORITY_FRAGMENT ") "
    2081             :     "WHERE host IN (SELECT host FROM moz_migrate_v32_temp) "
    2082           0 :   ), getter_AddRefs(updateHostsStmt));
    2083           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2084           0 :   nsCOMPtr<mozIStorageAsyncStatement> dropTableStmt;
    2085           0 :   rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
    2086             :     "DROP TABLE IF EXISTS moz_migrate_v32_temp"
    2087           0 :   ), getter_AddRefs(dropTableStmt));
    2088           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2089             : 
    2090             :   mozIStorageBaseStatement *stmts[] = {
    2091             :     expireOrphansStmt,
    2092             :     deleteHostsStmt,
    2093             :     updateHostsStmt,
    2094             :     dropTableStmt
    2095           0 :   };
    2096           0 :   nsCOMPtr<mozIStoragePendingStatement> ps;
    2097           0 :   rv = mMainConn->ExecuteAsync(stmts, ArrayLength(stmts), nullptr,
    2098           0 :                                getter_AddRefs(ps));
    2099           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2100             : 
    2101           0 :   return NS_OK;
    2102             : }
    2103             : 
    2104             : nsresult
    2105           0 : Database::MigrateV33Up() {
    2106           0 :   MOZ_ASSERT(NS_IsMainThread());
    2107             : 
    2108           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2109             :     "DROP INDEX IF EXISTS moz_places_url_uniqueindex"
    2110           0 :   ));
    2111           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2112             : 
    2113             :   // Add an url_hash column to moz_places.
    2114           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2115           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2116             :     "SELECT url_hash FROM moz_places"
    2117           0 :   ), getter_AddRefs(stmt));
    2118           0 :   if (NS_FAILED(rv)) {
    2119           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2120             :       "ALTER TABLE moz_places ADD COLUMN url_hash INTEGER DEFAULT 0 NOT NULL"
    2121           0 :     ));
    2122           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2123             :   }
    2124             : 
    2125           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2126             :     "UPDATE moz_places SET url_hash = hash(url) WHERE url_hash = 0"
    2127           0 :   ));
    2128           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2129             : 
    2130             :   // Create an index on url_hash.
    2131           0 :   rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_URL_HASH);
    2132           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2133             : 
    2134           0 :   return NS_OK;
    2135             : }
    2136             : 
    2137             : nsresult
    2138           0 : Database::MigrateV34Up() {
    2139           0 :   MOZ_ASSERT(NS_IsMainThread());
    2140             : 
    2141           0 :   nsresult rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2142             :     "DELETE FROM moz_keywords WHERE id IN ( "
    2143             :       "SELECT id FROM moz_keywords k "
    2144             :       "WHERE NOT EXISTS (SELECT 1 FROM moz_places h WHERE k.place_id = h.id) "
    2145             :     ")"
    2146           0 :   ));
    2147           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2148             : 
    2149           0 :   return NS_OK;
    2150             : }
    2151             : 
    2152             : nsresult
    2153           0 : Database::MigrateV35Up() {
    2154           0 :   MOZ_ASSERT(NS_IsMainThread());
    2155             : 
    2156           0 :   int64_t mobileRootId = CreateMobileRoot();
    2157           0 :   if (mobileRootId <= 0)  {
    2158             :     // Either the schema is broken or there isn't any root. The latter can
    2159             :     // happen if a consumer, for example Thunderbird, never used bookmarks.
    2160             :     // If there are no roots, this migration should not run.
    2161           0 :     nsCOMPtr<mozIStorageStatement> checkRootsStmt;
    2162           0 :     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2163             :       "SELECT id FROM moz_bookmarks WHERE parent = 0"
    2164           0 :     ), getter_AddRefs(checkRootsStmt));
    2165           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2166           0 :     mozStorageStatementScoper scoper(checkRootsStmt);
    2167           0 :     bool hasResult = false;
    2168           0 :     rv = checkRootsStmt->ExecuteStep(&hasResult);
    2169           0 :     if (NS_SUCCEEDED(rv) && !hasResult) {
    2170           0 :       return NS_OK;
    2171             :     }
    2172           0 :     return NS_ERROR_FAILURE;
    2173             :   }
    2174             : 
    2175             :   // At this point, we should have no more than two folders with the mobile
    2176             :   // bookmarks anno: the new root, and the old folder if one exists. If, for
    2177             :   // some reason, we have multiple folders with the anno, we append their
    2178             :   // children to the new root.
    2179           0 :   nsTArray<int64_t> folderIds;
    2180           0 :   nsresult rv = GetItemsWithAnno(NS_LITERAL_CSTRING(MOBILE_ROOT_ANNO),
    2181             :                                  nsINavBookmarksService::TYPE_FOLDER,
    2182           0 :                                  folderIds);
    2183           0 :   if (NS_FAILED(rv)) return rv;
    2184             : 
    2185           0 :   for (uint32_t i = 0; i < folderIds.Length(); ++i) {
    2186           0 :     if (folderIds[i] == mobileRootId) {
    2187             :       // Ignore the new mobile root. We'll remove this anno from the root in
    2188             :       // bug 1306445.
    2189           0 :       continue;
    2190             :     }
    2191             : 
    2192             :     // Append the folder's children to the new root.
    2193           0 :     nsCOMPtr<mozIStorageStatement> moveStmt;
    2194           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2195             :       "UPDATE moz_bookmarks "
    2196             :       "SET parent = :root_id, "
    2197             :           "position = position + IFNULL("
    2198             :             "(SELECT MAX(position) + 1 FROM moz_bookmarks "
    2199             :              "WHERE parent = :root_id), 0)"
    2200             :       "WHERE parent = :folder_id"
    2201           0 :     ), getter_AddRefs(moveStmt));
    2202           0 :     if (NS_FAILED(rv)) return rv;
    2203           0 :     mozStorageStatementScoper moveScoper(moveStmt);
    2204             : 
    2205           0 :     rv = moveStmt->BindInt64ByName(NS_LITERAL_CSTRING("root_id"),
    2206           0 :                                    mobileRootId);
    2207           0 :     if (NS_FAILED(rv)) return rv;
    2208           0 :     rv = moveStmt->BindInt64ByName(NS_LITERAL_CSTRING("folder_id"),
    2209           0 :                                    folderIds[i]);
    2210           0 :     if (NS_FAILED(rv)) return rv;
    2211             : 
    2212           0 :     rv = moveStmt->Execute();
    2213           0 :     if (NS_FAILED(rv)) return rv;
    2214             : 
    2215             :     // Delete the old folder.
    2216           0 :     rv = DeleteBookmarkItem(folderIds[i]);
    2217           0 :     if (NS_FAILED(rv)) return rv;
    2218             :   }
    2219             : 
    2220           0 :   return NS_OK;
    2221             : }
    2222             : 
    2223             : nsresult
    2224           0 : Database::MigrateV36Up() {
    2225           0 :   MOZ_ASSERT(NS_IsMainThread());
    2226             : 
    2227             :   // Add sync status and change counter tracking columns for bookmarks.
    2228           0 :   nsCOMPtr<mozIStorageStatement> syncStatusStmt;
    2229           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2230             :     "SELECT syncStatus FROM moz_bookmarks"
    2231           0 :   ), getter_AddRefs(syncStatusStmt));
    2232           0 :   if (NS_FAILED(rv)) {
    2233             :     // We default to SYNC_STATUS_UNKNOWN = 0 for existing bookmarks, matching
    2234             :     // the bookmark restore behavior. If Sync is set up, we'll update the status
    2235             :     // to SYNC_STATUS_NORMAL = 2 before the first post-migration sync.
    2236           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2237             :       "ALTER TABLE moz_bookmarks "
    2238             :       "ADD COLUMN syncStatus INTEGER DEFAULT 0 NOT NULL"
    2239           0 :     ));
    2240           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2241             :   }
    2242             : 
    2243           0 :   nsCOMPtr<mozIStorageStatement> syncChangeCounterStmt;
    2244           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2245             :     "SELECT syncChangeCounter FROM moz_bookmarks"
    2246           0 :   ), getter_AddRefs(syncChangeCounterStmt));
    2247           0 :   if (NS_FAILED(rv)) {
    2248             :     // The change counter starts at 1 for all local bookmarks. It's incremented
    2249             :     // for each modification that should trigger a sync, and decremented after
    2250             :     // the modified bookmark is uploaded to the server.
    2251           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2252             :       "ALTER TABLE moz_bookmarks "
    2253           0 :       "ADD COLUMN syncChangeCounter INTEGER DEFAULT 1 NOT NULL"));
    2254           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2255             :   }
    2256             : 
    2257           0 :   nsCOMPtr<mozIStorageStatement> tombstoneTableStmt;
    2258           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2259             :     "SELECT 1 FROM moz_bookmarks_deleted"
    2260           0 :   ), getter_AddRefs(tombstoneTableStmt));
    2261           0 :   if (NS_FAILED(rv)) {
    2262           0 :     rv = mMainConn->ExecuteSimpleSQL(CREATE_MOZ_BOOKMARKS_DELETED);
    2263           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2264             :   }
    2265             : 
    2266           0 :   return NS_OK;
    2267             : }
    2268             : 
    2269             : nsresult
    2270           0 : Database::MigrateV37Up() {
    2271           0 :   MOZ_ASSERT(NS_IsMainThread());
    2272             : 
    2273             :   // Move favicons to the new database.
    2274             :   // For now we retain the old moz_favicons table, but we empty it.
    2275             :   // This allows for a "safer" downgrade, even if icons will be lost in the
    2276             :   // process. In a couple versions we shall drop moz_favicons completely.
    2277             : 
    2278             :   // First, check if the old favicons table still exists.
    2279           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2280           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2281             :     "SELECT url FROM moz_favicons"
    2282           0 :   ), getter_AddRefs(stmt));
    2283           0 :   if (NS_FAILED(rv)) {
    2284             :     // The table has already been removed, nothing to do.
    2285           0 :     return NS_OK;
    2286             :   }
    2287             : 
    2288             :   // The new table accepts only png or svg payloads, so we set a valid width
    2289             :   // only for them, the mime-type for the others.  Later we will asynchronously
    2290             :   // try to convert the unsupported payloads, or remove them.
    2291             : 
    2292             :   // Add pages.
    2293           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2294             :     "INSERT INTO moz_pages_w_icons (page_url, page_url_hash) "
    2295             :     "SELECT h.url, hash(h.url) "
    2296             :     "FROM moz_places h "
    2297             :     "JOIN moz_favicons f ON f.id = h.favicon_id"
    2298           0 :   ));
    2299           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2300             :   // Set icons as expired, so we will replace them with proper versions at the
    2301             :   // first load.
    2302             :   // Note: we use a peculiarity of Sqlite here, where the column affinity
    2303             :   // is not enforced, thanks to that we can store a string in an integer column.
    2304           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2305             :     "INSERT INTO moz_icons (icon_url, fixed_icon_url_hash, width, data) "
    2306             :       "SELECT url, hash(fixup_url(url)), "
    2307             :              "(CASE WHEN mime_type = 'image/png' THEN 16 "
    2308             :                    "WHEN mime_type = 'image/svg+xml' THEN 65535 "
    2309             :                    "ELSE mime_type END), "
    2310             :              "data FROM moz_favicons "
    2311             :              "WHERE LENGTH(data) > 0 "
    2312           0 :   ));
    2313           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2314             :   // Create relations.
    2315           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2316             :     "INSERT OR IGNORE INTO moz_icons_to_pages (page_id, icon_id) "
    2317             :       "SELECT (SELECT id FROM moz_pages_w_icons "
    2318             :               "WHERE page_url_hash = h.url_hash "
    2319             :                 "AND page_url = h.url), "
    2320             :              "(SELECT id FROM moz_icons "
    2321             :               "WHERE fixed_icon_url_hash = hash(fixup_url(f.url)) "
    2322             :                 "AND icon_url = f.url) "
    2323             :       "FROM moz_favicons f "
    2324             :       "JOIN moz_places h on f.id = h.favicon_id"
    2325           0 :   ));
    2326           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2327             :   // Remove old favicons and relations.
    2328           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2329             :     "DELETE FROM moz_favicons"
    2330           0 :   ));
    2331           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2332             : 
    2333           0 :   rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2334             :     "UPDATE moz_places SET favicon_id = NULL"
    2335           0 :   ));
    2336           0 :   NS_ENSURE_SUCCESS(rv, rv);
    2337             : 
    2338             :   // Start the async conversion
    2339           0 :   nsFaviconService::ConvertUnsupportedPayloads(mMainConn);
    2340             : 
    2341           0 :   return NS_OK;
    2342             : }
    2343             : 
    2344             : nsresult
    2345           0 : Database::MigrateV38Up()
    2346             : {
    2347           0 :   MOZ_ASSERT(NS_IsMainThread());
    2348             : 
    2349           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2350           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2351             :     "SELECT description, preview_image_url FROM moz_places"
    2352           0 :   ), getter_AddRefs(stmt));
    2353           0 :   if (NS_FAILED(rv)) {
    2354           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2355             :       "ALTER TABLE moz_places ADD COLUMN description TEXT"
    2356           0 :     ));
    2357           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2358             : 
    2359           0 :     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
    2360             :       "ALTER TABLE moz_places ADD COLUMN preview_image_url TEXT"
    2361           0 :     ));
    2362           0 :     NS_ENSURE_SUCCESS(rv, rv);
    2363             :   }
    2364             : 
    2365           0 :   return NS_OK;
    2366             : }
    2367             : 
    2368             : nsresult
    2369           0 : Database::GetItemsWithAnno(const nsACString& aAnnoName, int32_t aItemType,
    2370             :                            nsTArray<int64_t>& aItemIds)
    2371             : {
    2372           0 :   nsCOMPtr<mozIStorageStatement> stmt;
    2373           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2374             :     "SELECT b.id FROM moz_items_annos a "
    2375             :     "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id "
    2376             :     "JOIN moz_bookmarks b ON b.id = a.item_id "
    2377             :     "WHERE n.name = :anno_name AND "
    2378             :           "b.type = :item_type"
    2379           0 :   ), getter_AddRefs(stmt));
    2380           0 :   if (NS_FAILED(rv)) return rv;
    2381           0 :   mozStorageStatementScoper scoper(stmt);
    2382             : 
    2383           0 :   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("anno_name"), aAnnoName);
    2384           0 :   if (NS_FAILED(rv)) return rv;
    2385           0 :   rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("item_type"), aItemType);
    2386           0 :   if (NS_FAILED(rv)) return rv;
    2387             : 
    2388           0 :   bool hasMore = false;
    2389           0 :   while (NS_SUCCEEDED(stmt->ExecuteStep(&hasMore)) && hasMore) {
    2390             :     int64_t itemId;
    2391           0 :     rv = stmt->GetInt64(0, &itemId);
    2392           0 :     if (NS_FAILED(rv)) return rv;
    2393           0 :     aItemIds.AppendElement(itemId);
    2394             :   }
    2395             : 
    2396           0 :   return NS_OK;
    2397             : }
    2398             : 
    2399             : nsresult
    2400           0 : Database::DeleteBookmarkItem(int32_t aItemId)
    2401             : {
    2402             :   // Delete the old bookmark.
    2403           0 :   nsCOMPtr<mozIStorageStatement> deleteStmt;
    2404           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2405             :     "DELETE FROM moz_bookmarks WHERE id = :item_id"
    2406           0 :   ), getter_AddRefs(deleteStmt));
    2407           0 :   if (NS_FAILED(rv)) return rv;
    2408           0 :   mozStorageStatementScoper deleteScoper(deleteStmt);
    2409             : 
    2410           0 :   rv = deleteStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
    2411           0 :                                    aItemId);
    2412           0 :   if (NS_FAILED(rv)) return rv;
    2413             : 
    2414           0 :   rv = deleteStmt->Execute();
    2415           0 :   if (NS_FAILED(rv)) return rv;
    2416             : 
    2417             :   // Clean up orphan annotations.
    2418           0 :   nsCOMPtr<mozIStorageStatement> removeAnnosStmt;
    2419           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2420             :     "DELETE FROM moz_items_annos WHERE item_id = :item_id"
    2421           0 :   ), getter_AddRefs(removeAnnosStmt));
    2422           0 :   if (NS_FAILED(rv)) return rv;
    2423           0 :   mozStorageStatementScoper removeAnnosScoper(removeAnnosStmt);
    2424             : 
    2425           0 :   rv = removeAnnosStmt->BindInt64ByName(NS_LITERAL_CSTRING("item_id"),
    2426           0 :                                         aItemId);
    2427           0 :   if (NS_FAILED(rv)) return rv;
    2428             : 
    2429           0 :   rv = removeAnnosStmt->Execute();
    2430           0 :   if (NS_FAILED(rv)) return rv;
    2431             : 
    2432           0 :   return NS_OK;
    2433             : }
    2434             : 
    2435             : int64_t
    2436           0 : Database::CreateMobileRoot()
    2437             : {
    2438           0 :   MOZ_ASSERT(NS_IsMainThread());
    2439             : 
    2440             :   // Create the mobile root, ignoring conflicts if one already exists (for
    2441             :   // example, if the user downgraded to an earlier release channel).
    2442           0 :   nsCOMPtr<mozIStorageStatement> createStmt;
    2443           0 :   nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2444             :     "INSERT OR IGNORE INTO moz_bookmarks "
    2445             :       "(type, title, dateAdded, lastModified, guid, position, parent) "
    2446             :     "SELECT :item_type, :item_title, :timestamp, :timestamp, :guid, "
    2447             :       "(SELECT COUNT(*) FROM moz_bookmarks p WHERE p.parent = b.id), b.id "
    2448             :     "FROM moz_bookmarks b WHERE b.parent = 0"
    2449           0 :   ), getter_AddRefs(createStmt));
    2450           0 :   if (NS_FAILED(rv)) return -1;
    2451           0 :   mozStorageStatementScoper createScoper(createStmt);
    2452             : 
    2453           0 :   rv = createStmt->BindInt32ByName(NS_LITERAL_CSTRING("item_type"),
    2454           0 :                                    nsINavBookmarksService::TYPE_FOLDER);
    2455           0 :   if (NS_FAILED(rv)) return -1;
    2456           0 :   rv = createStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("item_title"),
    2457           0 :                                         NS_LITERAL_CSTRING(MOBILE_ROOT_TITLE));
    2458           0 :   if (NS_FAILED(rv)) return -1;
    2459           0 :   rv = createStmt->BindInt64ByName(NS_LITERAL_CSTRING("timestamp"),
    2460           0 :                                    RoundedPRNow());
    2461           0 :   if (NS_FAILED(rv)) return -1;
    2462           0 :   rv = createStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
    2463           0 :                                         NS_LITERAL_CSTRING(MOBILE_ROOT_GUID));
    2464           0 :   if (NS_FAILED(rv)) return -1;
    2465             : 
    2466           0 :   rv = createStmt->Execute();
    2467           0 :   if (NS_FAILED(rv)) return -1;
    2468             : 
    2469             :   // Find the mobile root ID. We can't use the last inserted ID because the
    2470             :   // root might already exist, and we ignore on conflict.
    2471           0 :   nsCOMPtr<mozIStorageStatement> findIdStmt;
    2472           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2473             :     "SELECT id FROM moz_bookmarks WHERE guid = :guid"
    2474           0 :   ), getter_AddRefs(findIdStmt));
    2475           0 :   if (NS_FAILED(rv)) return -1;
    2476           0 :   mozStorageStatementScoper findIdScoper(findIdStmt);
    2477             : 
    2478           0 :   rv = findIdStmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"),
    2479           0 :                                         NS_LITERAL_CSTRING(MOBILE_ROOT_GUID));
    2480           0 :   if (NS_FAILED(rv)) return -1;
    2481             : 
    2482           0 :   bool hasResult = false;
    2483           0 :   rv = findIdStmt->ExecuteStep(&hasResult);
    2484           0 :   if (NS_FAILED(rv) || !hasResult) return -1;
    2485             : 
    2486             :   int64_t rootId;
    2487           0 :   rv = findIdStmt->GetInt64(0, &rootId);
    2488           0 :   if (NS_FAILED(rv)) return -1;
    2489             : 
    2490             :   // Set the mobile bookmarks anno on the new root, so that Sync code on an
    2491             :   // older channel can still find it in case of a downgrade. This can be
    2492             :   // removed in bug 1306445.
    2493           0 :   nsCOMPtr<mozIStorageStatement> addAnnoNameStmt;
    2494           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2495             :     "INSERT OR IGNORE INTO moz_anno_attributes (name) VALUES (:anno_name)"
    2496           0 :   ), getter_AddRefs(addAnnoNameStmt));
    2497           0 :   if (NS_FAILED(rv)) return -1;
    2498           0 :   mozStorageStatementScoper addAnnoNameScoper(addAnnoNameStmt);
    2499             : 
    2500           0 :   rv = addAnnoNameStmt->BindUTF8StringByName(
    2501           0 :     NS_LITERAL_CSTRING("anno_name"), NS_LITERAL_CSTRING(MOBILE_ROOT_ANNO));
    2502           0 :   if (NS_FAILED(rv)) return -1;
    2503           0 :   rv = addAnnoNameStmt->Execute();
    2504           0 :   if (NS_FAILED(rv)) return -1;
    2505             : 
    2506           0 :   nsCOMPtr<mozIStorageStatement> addAnnoStmt;
    2507           0 :   rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2508             :     "INSERT OR IGNORE INTO moz_items_annos "
    2509             :       "(id, item_id, anno_attribute_id, content, flags, "
    2510             :        "expiration, type, dateAdded, lastModified) "
    2511             :     "SELECT "
    2512             :       "(SELECT a.id FROM moz_items_annos a "
    2513             :        "WHERE a.anno_attribute_id = n.id AND "
    2514             :              "a.item_id = :root_id), "
    2515             :       ":root_id, n.id, 1, 0, :expiration, :type, :timestamp, :timestamp "
    2516             :     "FROM moz_anno_attributes n WHERE name = :anno_name"
    2517           0 :   ), getter_AddRefs(addAnnoStmt));
    2518           0 :   if (NS_FAILED(rv)) return -1;
    2519           0 :   mozStorageStatementScoper addAnnoScoper(addAnnoStmt);
    2520             : 
    2521           0 :   rv = addAnnoStmt->BindInt64ByName(NS_LITERAL_CSTRING("root_id"), rootId);
    2522           0 :   if (NS_FAILED(rv)) return -1;
    2523           0 :   rv = addAnnoStmt->BindUTF8StringByName(
    2524           0 :     NS_LITERAL_CSTRING("anno_name"), NS_LITERAL_CSTRING(MOBILE_ROOT_ANNO));
    2525           0 :   if (NS_FAILED(rv)) return -1;
    2526           0 :   rv = addAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("expiration"),
    2527           0 :                                     nsIAnnotationService::EXPIRE_NEVER);
    2528           0 :   if (NS_FAILED(rv)) return -1;
    2529           0 :   rv = addAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("type"),
    2530           0 :                                     nsIAnnotationService::TYPE_INT32);
    2531           0 :   if (NS_FAILED(rv)) return -1;
    2532           0 :   rv = addAnnoStmt->BindInt32ByName(NS_LITERAL_CSTRING("timestamp"),
    2533           0 :                                     RoundedPRNow());
    2534           0 :   if (NS_FAILED(rv)) return -1;
    2535             : 
    2536           0 :   rv = addAnnoStmt->Execute();
    2537           0 :   if (NS_FAILED(rv)) return -1;
    2538             : 
    2539           0 :   return rootId;
    2540             : }
    2541             : 
    2542             : void
    2543           0 : Database::Shutdown()
    2544             : {
    2545             :   // As the last step in the shutdown path, finalize the database handle.
    2546           0 :   MOZ_ASSERT(NS_IsMainThread());
    2547           0 :   MOZ_ASSERT(!mClosed);
    2548             : 
    2549             :   // Break cycles with the shutdown blockers.
    2550           0 :   mClientsShutdown = nullptr;
    2551           0 :   nsCOMPtr<mozIStorageCompletionCallback> connectionShutdown = mConnectionShutdown.forget();
    2552             : 
    2553           0 :   if (!mMainConn) {
    2554             :     // The connection has never been initialized. Just mark it as closed.
    2555           0 :     mClosed = true;
    2556           0 :     (void)connectionShutdown->Complete(NS_OK, nullptr);
    2557           0 :     return;
    2558             :   }
    2559             : 
    2560             : #ifdef DEBUG
    2561             :   {
    2562             :     bool hasResult;
    2563           0 :     nsCOMPtr<mozIStorageStatement> stmt;
    2564             : 
    2565             :     // Sanity check for missing guids.
    2566           0 :     nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2567             :       "SELECT 1 "
    2568             :       "FROM moz_places "
    2569             :       "WHERE guid IS NULL "
    2570           0 :     ), getter_AddRefs(stmt));
    2571           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2572           0 :     rv = stmt->ExecuteStep(&hasResult);
    2573           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2574           0 :     MOZ_ASSERT(!hasResult, "Found a page without a GUID!");
    2575           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2576             :       "SELECT 1 "
    2577             :       "FROM moz_bookmarks "
    2578             :       "WHERE guid IS NULL "
    2579           0 :     ), getter_AddRefs(stmt));
    2580           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2581           0 :     rv = stmt->ExecuteStep(&hasResult);
    2582           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2583           0 :     MOZ_ASSERT(!hasResult, "Found a bookmark without a GUID!");
    2584             : 
    2585             :     // Sanity check for unrounded dateAdded and lastModified values (bug
    2586             :     // 1107308).
    2587           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2588             :         "SELECT 1 "
    2589             :         "FROM moz_bookmarks "
    2590             :         "WHERE dateAdded % 1000 > 0 OR lastModified % 1000 > 0 LIMIT 1"
    2591           0 :       ), getter_AddRefs(stmt));
    2592           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2593           0 :     rv = stmt->ExecuteStep(&hasResult);
    2594           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2595           0 :     MOZ_ASSERT(!hasResult, "Found unrounded dates!");
    2596             : 
    2597             :     // Sanity check url_hash
    2598           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2599             :       "SELECT 1 FROM moz_places WHERE url_hash = 0"
    2600           0 :     ), getter_AddRefs(stmt));
    2601           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2602           0 :     rv = stmt->ExecuteStep(&hasResult);
    2603           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2604           0 :     MOZ_ASSERT(!hasResult, "Found a place without a hash!");
    2605             : 
    2606             :     // Sanity check unique urls
    2607           0 :     rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
    2608             :       "SELECT 1 FROM moz_places GROUP BY url HAVING count(*) > 1 "
    2609           0 :     ), getter_AddRefs(stmt));
    2610           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2611           0 :     rv = stmt->ExecuteStep(&hasResult);
    2612           0 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    2613           0 :     MOZ_ASSERT(!hasResult, "Found a duplicate url!");
    2614             :   }
    2615             : #endif
    2616             : 
    2617           0 :   mMainThreadStatements.FinalizeStatements();
    2618           0 :   mMainThreadAsyncStatements.FinalizeStatements();
    2619             : 
    2620             :   RefPtr< FinalizeStatementCacheProxy<mozIStorageStatement> > event =
    2621             :     new FinalizeStatementCacheProxy<mozIStorageStatement>(
    2622             :           mAsyncThreadStatements,
    2623             :           NS_ISUPPORTS_CAST(nsIObserver*, this)
    2624           0 :         );
    2625           0 :   DispatchToAsyncThread(event);
    2626             : 
    2627           0 :   mClosed = true;
    2628             : 
    2629           0 :   (void)mMainConn->AsyncClose(connectionShutdown);
    2630           0 :   mMainConn = nullptr;
    2631             : }
    2632             : 
    2633             : ////////////////////////////////////////////////////////////////////////////////
    2634             : //// nsIObserver
    2635             : 
    2636             : NS_IMETHODIMP
    2637           0 : Database::Observe(nsISupports *aSubject,
    2638             :                   const char *aTopic,
    2639             :                   const char16_t *aData)
    2640             : {
    2641           0 :   MOZ_ASSERT(NS_IsMainThread());
    2642           0 :   if (strcmp(aTopic, TOPIC_PROFILE_CHANGE_TEARDOWN) == 0) {
    2643             :     // Tests simulating shutdown may cause multiple notifications.
    2644           0 :     if (IsShutdownStarted()) {
    2645           0 :       return NS_OK;
    2646             :     }
    2647             : 
    2648           0 :     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
    2649           0 :     NS_ENSURE_STATE(os);
    2650             : 
    2651             :     // If shutdown happens in the same mainthread loop as init, observers could
    2652             :     // handle the places-init-complete notification after xpcom-shutdown, when
    2653             :     // the connection does not exist anymore.  Removing those observers would
    2654             :     // be less expensive but may cause their RemoveObserver calls to throw.
    2655             :     // Thus notify the topic now, so they stop listening for it.
    2656           0 :     nsCOMPtr<nsISimpleEnumerator> e;
    2657           0 :     if (NS_SUCCEEDED(os->EnumerateObservers(TOPIC_PLACES_INIT_COMPLETE,
    2658           0 :                      getter_AddRefs(e))) && e) {
    2659           0 :       bool hasMore = false;
    2660           0 :       while (NS_SUCCEEDED(e->HasMoreElements(&hasMore)) && hasMore) {
    2661           0 :         nsCOMPtr<nsISupports> supports;
    2662           0 :         if (NS_SUCCEEDED(e->GetNext(getter_AddRefs(supports)))) {
    2663           0 :           nsCOMPtr<nsIObserver> observer = do_QueryInterface(supports);
    2664           0 :           (void)observer->Observe(observer, TOPIC_PLACES_INIT_COMPLETE, nullptr);
    2665             :         }
    2666             :       }
    2667             :     }
    2668             : 
    2669             :     // Notify all Places users that we are about to shutdown.
    2670           0 :     (void)os->NotifyObservers(nullptr, TOPIC_PLACES_SHUTDOWN, nullptr);
    2671           0 :   } else if (strcmp(aTopic, TOPIC_SIMULATE_PLACES_SHUTDOWN) == 0) {
    2672             :     // This notification is (and must be) only used by tests that are trying
    2673             :     // to simulate Places shutdown out of the normal shutdown path.
    2674             : 
    2675             :     // Tests simulating shutdown may cause re-entrance.
    2676           0 :     if (IsShutdownStarted()) {
    2677           0 :       return NS_OK;
    2678             :     }
    2679             : 
    2680             :     // We are simulating a shutdown, so invoke the shutdown blockers,
    2681             :     // wait for them, then proceed with connection shutdown.
    2682             :     // Since we are already going through shutdown, but it's not the real one,
    2683             :     // we won't need to block the real one anymore, so we can unblock it.
    2684             :     {
    2685           0 :       nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetProfileChangeTeardownPhase();
    2686           0 :       if (shutdownPhase) {
    2687           0 :         shutdownPhase->RemoveBlocker(mClientsShutdown.get());
    2688             :       }
    2689           0 :       (void)mClientsShutdown->BlockShutdown(nullptr);
    2690             :     }
    2691             : 
    2692             :     // Spin the events loop until the clients are done.
    2693             :     // Note, this is just for tests, specifically test_clearHistory_shutdown.js
    2694           0 :     SpinEventLoopUntil([&]() {
    2695           0 :       return mClientsShutdown->State() == PlacesShutdownBlocker::States::RECEIVED_DONE;
    2696           0 :     });
    2697             : 
    2698             :     {
    2699           0 :       nsCOMPtr<nsIAsyncShutdownClient> shutdownPhase = GetProfileBeforeChangePhase();
    2700           0 :       if (shutdownPhase) {
    2701           0 :         shutdownPhase->RemoveBlocker(mConnectionShutdown.get());
    2702             :       }
    2703           0 :       (void)mConnectionShutdown->BlockShutdown(nullptr);
    2704             :     }
    2705             :   }
    2706           0 :   return NS_OK;
    2707             : }
    2708             : 
    2709             : uint32_t
    2710           4 : Database::MaxUrlLength() {
    2711           4 :   MOZ_ASSERT(NS_IsMainThread());
    2712           4 :   if (!mMaxUrlLength) {
    2713           1 :     mMaxUrlLength = Preferences::GetInt(PREF_HISTORY_MAXURLLEN,
    2714             :                                         PREF_HISTORY_MAXURLLEN_DEFAULT);
    2715           1 :     if (mMaxUrlLength < 255 || mMaxUrlLength > INT32_MAX) {
    2716           0 :       mMaxUrlLength = PREF_HISTORY_MAXURLLEN_DEFAULT;
    2717             :     }
    2718             :   }
    2719           4 :   return mMaxUrlLength;
    2720             : }
    2721             : 
    2722             : 
    2723             : 
    2724             : } // namespace places
    2725             : } // namespace mozilla

Generated by: LCOV version 1.13