LCOV - code coverage report
Current view: directory - toolkit/components/places - Database.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 930 881 94.7 %
Date: 2012-06-02 Functions: 39 39 100.0 %

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

Generated by: LCOV version 1.7