Bug 675996 - Part 1: extend moz_favicons with GUID to support Sync. r=mak
authorRichard Newman <rnewman@mozilla.com>
Thu, 01 Dec 2011 13:58:19 -0800
changeset 82761 9d2dd9486d41828ae6c19478a9e228632d2a34ea
parent 82760 25081a39d0bb4b32219f5e3c0784619a37e7e70c
child 82762 8c47ef9419acbaa82366586f36ecfa95724ae444
push id519
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 00:38:35 +0000
treeherdermozilla-beta@788ea1ef610b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmak
bugs675996
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 675996 - Part 1: extend moz_favicons with GUID to support Sync. r=mak
toolkit/components/places/AsyncFaviconHelpers.cpp
toolkit/components/places/AsyncFaviconHelpers.h
toolkit/components/places/Database.cpp
toolkit/components/places/Database.h
toolkit/components/places/nsFaviconService.cpp
toolkit/components/places/nsNavHistory.cpp
toolkit/components/places/nsPlacesIndexes.h
toolkit/components/places/nsPlacesTables.h
--- a/toolkit/components/places/AsyncFaviconHelpers.cpp
+++ b/toolkit/components/places/AsyncFaviconHelpers.cpp
@@ -16,16 +16,17 @@
  * The Original Code is Places.
  *
  * The Initial Developer of the Original Code is the Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2010
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Marco Bonardo <mak77@bonardo.net> (original author)
+ *   Richard Newman <rnewman@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -719,33 +720,50 @@ AsyncAssociateIconToPage::Run()
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   mozStorageTransaction transaction(mDB->MainConn(), false,
                                     mozIStorageConnection::TRANSACTION_IMMEDIATE);
 
   // If there is no entry for this icon, or the entry is obsolete, replace it.
   if (mIcon.id == 0 || (mIcon.status & ICON_STATUS_CHANGED)) {
+    // The 'multi-coalesce' here ensures that replacing a favicon without
+    // specifying a :guid parameter doesn't cause it to be allocated a new
+    // GUID.
     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
       "INSERT OR REPLACE INTO moz_favicons "
-        "(id, url, data, mime_type, expiration) "
+        "(id, url, data, mime_type, expiration, guid) "
       "VALUES ((SELECT id FROM moz_favicons WHERE url = :icon_url), "
-              ":icon_url, :data, :mime_type, :expiration) "
+              ":icon_url, :data, :mime_type, :expiration, "
+              "COALESCE(:guid, "
+                       "(SELECT guid FROM moz_favicons "
+                        "WHERE url = :icon_url), "
+                       "GENERATE_GUID()))"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
     rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), mIcon.spec);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindBlobByName(NS_LITERAL_CSTRING("data"),
                               TO_INTBUFFER(mIcon.data), mIcon.data.Length());
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), mIcon.mimeType);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), mIcon.expiration);
     NS_ENSURE_SUCCESS(rv, rv);
+
+    // Binding a GUID allows us to override the current (or generated) GUID.
+    if (mIcon.guid.IsEmpty()) {
+      rv = stmt->BindNullByName(NS_LITERAL_CSTRING("guid"));
+    }
+    else {
+      rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("guid"), mIcon.guid);
+    }
+    NS_ENSURE_SUCCESS(rv, rv);
+
     rv = stmt->Execute();
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Get the new icon id.  Do this regardless mIcon.id, since other code
     // could have added a entry before us.  Indeed we interrupted the thread
     // after the previous call to FetchIconInfo.
     rv = FetchIconInfo(mDB, mIcon);
     NS_ENSURE_SUCCESS(rv, rv);
--- a/toolkit/components/places/AsyncFaviconHelpers.h
+++ b/toolkit/components/places/AsyncFaviconHelpers.h
@@ -70,25 +70,27 @@ enum AsyncFaviconFetchMode {
 struct IconData
 {
   IconData()
   : id(0)
   , expiration(0)
   , fetchMode(FETCH_NEVER)
   , status(ICON_STATUS_UNKNOWN)
   {
+    guid.SetIsVoid(PR_TRUE);
   }
 
   PRInt64 id;
   nsCString spec;
   nsCString data;
   nsCString mimeType;
   PRTime expiration;
   enum AsyncFaviconFetchMode fetchMode;
   PRUint16 status; // This is a bitset, see ICON_STATUS_* defines above.
+  nsCString guid;
 };
 
 /**
  * Data cache for a page entry.
  */
 struct PageData
 {
   PageData()
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -28,16 +28,17 @@
  *   Asaf Romano <mano@mozilla.com>
  *   Marco Bonardo <mak77@bonardo.net>
  *   Edward Lee <edward.lee@engineering.uiuc.edu>
  *   Michael Ventnor <m.ventnor@gmail.com>
  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
  *   Drew Willcoxon <adw@mozilla.com>
  *   Philipp von Weitershausen <philipp@weitershausen.de>
  *   Paolo Amadini <http://www.amadzone.org/>
+ *   Richard Newman <rnewman@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -637,17 +638,23 @@ Database::InitSchema(bool* aDatabaseMigr
       // Firefox 4 uses schema version 11.
 
       // Firefox 8 uses schema version 12.
 
       if (currentSchemaVersion < 13) {
         rv = MigrateV13Up();
         NS_ENSURE_SUCCESS(rv, rv);
       }
-      // Firefox 11 uses schema version 13.
+
+      if (currentSchemaVersion < 14) {
+        rv = MigrateV14Up();
+        NS_ENSURE_SUCCESS(rv, rv);
+      }
+
+      // Firefox 11 uses schema version 14.
 
       // Schema Upgrades must add migration code here.
     }
   }
   else {
     // This is a new database, so we have to create all the tables and indices.
 
     // moz_places.
@@ -945,17 +952,17 @@ Database::MigrateV7Up()
   NS_ENSURE_SUCCESS(rv, rv);
   if (!URLUniqueIndexExists) {
     return NS_ERROR_FILE_CORRUPTED;
   }
 
   mozStorageTransaction transaction(mMainConn, false);
 
   // We need an index on lastModified to catch quickly last modified bookmark
-  // title for tag container's children. This will be useful for sync too.
+  // title for tag container's children. This will be useful for Sync, too.
   bool lastModIndexExists = false;
   rv = mMainConn->IndexExists(
     NS_LITERAL_CSTRING("moz_bookmarks_itemlastmodifiedindex"),
     &lastModIndexExists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!lastModIndexExists) {
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_PLACELASTMODIFIED);
@@ -1277,18 +1284,18 @@ Database::MigrateV11Up()
       "ALTER TABLE moz_bookmarks "
       "ADD COLUMN guid TEXT"
     ));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_BOOKMARKS_GUID);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    // moz_placess grew a guid column.  Add the column, but do not populate it
-    // with anything just yet.  We will do that soon.
+    // moz_places grew a guid column. Add the column, but do not populate it
+    // with anything just yet. We will do that soon.
     rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
       "ALTER TABLE moz_places "
       "ADD COLUMN guid TEXT"
     ));
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_PLACES_GUID);
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1301,35 +1308,65 @@ Database::MigrateV11Up()
   return NS_OK;
 }
 
 nsresult
 Database::MigrateV13Up()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  // Dynamic containers are no more supported.
-
-  // For existing profiles, we may not have a moz_bookmarks.guid column
+  // Dynamic containers are no longer supported.
   nsCOMPtr<mozIStorageAsyncStatement> deleteDynContainersStmt;
   nsresult rv = mMainConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
       "DELETE FROM moz_bookmarks WHERE type = :item_type"),
     getter_AddRefs(deleteDynContainersStmt));
   rv = deleteDynContainersStmt->BindInt32ByName(
     NS_LITERAL_CSTRING("item_type"),
     nsINavBookmarksService::TYPE_DYNAMIC_CONTAINER
   );
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<mozIStoragePendingStatement> ps;
   rv = deleteDynContainersStmt->ExecuteAsync(nsnull, getter_AddRefs(ps));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
+nsresult
+Database::MigrateV14Up()
+{
+  // For existing profiles, we may not have a moz_favicons.guid column.
+  // Add it here. We want it to be unique, but ALTER TABLE doesn't allow
+  // a uniqueness constraint, so the index must be created separately.
+  nsCOMPtr<mozIStorageStatement> hasGuidStatement;
+  nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
+      "SELECT guid FROM moz_favicons"),
+    getter_AddRefs(hasGuidStatement));
+
+  if (NS_FAILED(rv)) {
+    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "ALTER TABLE moz_favicons "
+      "ADD COLUMN guid TEXT"
+    ));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // Generate GUIDs for our existing favicons.
+    rv = mMainConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
+      "UPDATE moz_favicons "
+      "SET guid = GENERATE_GUID()"
+    ));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // And now we can make the column unique.
+    rv = mMainConn->ExecuteSimpleSQL(CREATE_IDX_MOZ_FAVICONS_GUID);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
 void
 Database::Shutdown()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mShuttingDown);
 
   mMainThreadStatements.FinalizeStatements();
   mMainThreadAsyncStatements.FinalizeStatements();
@@ -1407,16 +1444,20 @@ Database::Observe(nsISupports *aSubject,
       nsresult rv = mMainConn->CreateStatement(NS_LITERAL_CSTRING(
         "SELECT 1 "
         "FROM moz_places "
         "WHERE guid IS NULL "
         "UNION ALL "
         "SELECT 1 "
         "FROM moz_bookmarks "
         "WHERE guid IS NULL "
+        "UNION ALL "
+        "SELECT 1 "
+        "FROM moz_favicons "
+        "WHERE guid IS NULL "
       ), getter_AddRefs(stmt));
       NS_ENSURE_SUCCESS(rv, rv);
 
       bool haveNullGuids;
       rv = stmt->ExecuteStep(&haveNullGuids);
       NS_ENSURE_SUCCESS(rv, rv);
       NS_ASSERTION(!haveNullGuids,
                    "Someone added an entry without adding a GUID!");
--- a/toolkit/components/places/Database.h
+++ b/toolkit/components/places/Database.h
@@ -40,19 +40,19 @@
 
 #include "nsThreadUtils.h"
 #include "nsWeakReference.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIObserver.h"
 #include "mozilla/storage.h"
 #include "mozilla/storage/StatementCache.h"
 
-// This is the schema version, update it at any schema change and add a
+// This is the schema version. Update it at any schema change and add a
 // corresponding migrateVxx method below.
-#define DATABASE_SCHEMA_VERSION 13
+#define DATABASE_SCHEMA_VERSION 14
 
 // Fired after Places inited.
 #define TOPIC_PLACES_INIT_COMPLETE "places-init-complete"
 // Fired when initialization fails due to a locked database.
 #define TOPIC_DATABASE_LOCKED "places-database-locked"
 // This topic is received when the profile is about to be lost.  Places does
 // initial shutdown work and notifies TOPIC_PLACES_SHUTDOWN to all listeners.
 // Any shutdown work that requires the Places APIs should happen here.
@@ -283,18 +283,20 @@ protected:
   /**
    * Helpers used by schema upgrades.
    */
   nsresult MigrateV7Up();
   nsresult MigrateV8Up();
   nsresult MigrateV9Up();
   nsresult MigrateV10Up();
   nsresult MigrateV11Up();
+  nsresult MigrateV13Up();
+  nsresult MigrateV14Up();
+
   nsresult CheckAndUpdateGUIDs();
-  nsresult MigrateV13Up();
 
 private:
   ~Database();
 
   /**
    * Singleton getter, invoked by class instantiation.
    *
    * Note: does AddRef.
--- a/toolkit/components/places/nsFaviconService.cpp
+++ b/toolkit/components/places/nsFaviconService.cpp
@@ -233,22 +233,25 @@ nsFaviconService::SetFaviconUrlForPage(n
     }
   }
 
   mozStorageTransaction transaction(mDB->MainConn(), false);
 
   if (iconId == -1) {
     // We did not find any entry for this icon, so create a new one.
     nsCOMPtr<mozIStorageStatement> stmt = mDB->GetStatement(
-      "INSERT INTO moz_favicons (id, url, data, mime_type, expiration) "
-      "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration)"
+      "INSERT INTO moz_favicons (id, url, data, mime_type, expiration, guid) "
+      "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration, "
+              "COALESCE(:guid, GENERATE_GUID()))"
     );
     NS_ENSURE_STATE(stmt);
     mozStorageStatementScoper scoper(stmt);
 
+    rv = stmt->BindNullByName(NS_LITERAL_CSTRING("guid"));
+    NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("icon_id"));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = URIBinder::Bind(stmt, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("data"));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = stmt->BindNullByName(NS_LITERAL_CSTRING("mime_type"));
     NS_ENSURE_SUCCESS(rv, rv);
@@ -438,49 +441,56 @@ nsFaviconService::SetFaviconData(nsIURI*
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (hasResult) {
       // Get id of the old entry and update it.
       PRInt64 id;
       rv = stmt->GetInt64(0, &id);
       NS_ENSURE_SUCCESS(rv, rv);
       statement = mDB->GetStatement(
-        "UPDATE moz_favicons SET data = :data, mime_type = :mime_type, "
-                                "expiration = :expiration "
+        "UPDATE moz_favicons SET "
+               "guid       = COALESCE(:guid, guid), "
+               "data       = :data, "
+               "mime_type  = :mime_type, "
+               "expiration = :expiration "
         "WHERE id = :icon_id"
       );
       NS_ENSURE_STATE(statement);
 
+      rv = statement->BindNullByName(NS_LITERAL_CSTRING("guid"));
+      NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("icon_id"), id);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindBlobByName(NS_LITERAL_CSTRING("data"), data, dataLen);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), *mimeType);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aExpiration);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else {
       // Insert a new entry.
       statement = mDB->GetStatement(
-        "INSERT INTO moz_favicons (id, url, data, mime_type, expiration) "
-        "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration)"
-      );
+       "INSERT INTO moz_favicons (id, url, data, mime_type, expiration, guid) "
+       "VALUES (:icon_id, :icon_url, :data, :mime_type, :expiration, "
+               "COALESCE(:guid, GENERATE_GUID()))");
       NS_ENSURE_STATE(statement);
 
       rv = statement->BindNullByName(NS_LITERAL_CSTRING("icon_id"));
       NS_ENSURE_SUCCESS(rv, rv);
       rv = URIBinder::Bind(statement, NS_LITERAL_CSTRING("icon_url"), aFaviconURI);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindBlobByName(NS_LITERAL_CSTRING("data"), data, dataLen);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindUTF8StringByName(NS_LITERAL_CSTRING("mime_type"), *mimeType);
       NS_ENSURE_SUCCESS(rv, rv);
       rv = statement->BindInt64ByName(NS_LITERAL_CSTRING("expiration"), aExpiration);
       NS_ENSURE_SUCCESS(rv, rv);
+      rv = statement->BindNullByName(NS_LITERAL_CSTRING("guid"));
+      NS_ENSURE_SUCCESS(rv, rv);
     }
   }
   mozStorageStatementScoper statementScoper(statement);
 
   rv = statement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -25,16 +25,17 @@
  *   Asaf Romano <mano@mozilla.com>
  *   Marco Bonardo <mak77@bonardo.net>
  *   Edward Lee <edward.lee@engineering.uiuc.edu>
  *   Michael Ventnor <m.ventnor@gmail.com>
  *   Ehsan Akhgari <ehsan.akhgari@gmail.com>
  *   Drew Willcoxon <adw@mozilla.com>
  *   Philipp von Weitershausen <philipp@weitershausen.de>
  *   Paolo Amadini <http://www.amadzone.org/>
+ *   Richard Newman <rnewman@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
--- a/toolkit/components/places/nsPlacesIndexes.h
+++ b/toolkit/components/places/nsPlacesIndexes.h
@@ -140,10 +140,18 @@
  * moz_items_annos
  */
 
 #define CREATE_IDX_MOZ_ITEMSANNOS_PLACEATTRIBUTE \
   CREATE_PLACES_IDX( \
     "itemattributeindex", "moz_items_annos", "item_id, anno_attribute_id", "UNIQUE" \
   )
 
+/**
+ * moz_favicons
+ */
+
+#define CREATE_IDX_MOZ_FAVICONS_GUID \
+  CREATE_PLACES_IDX( \
+    "guid_uniqueindex", "moz_favicons", "guid", "UNIQUE" \
+  )
 
 #endif // nsPlacesIndexes_h__
--- a/toolkit/components/places/nsPlacesTables.h
+++ b/toolkit/components/places/nsPlacesTables.h
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  * Mozilla Corporation.
  * Portions created by the Initial Developer are Copyright (C) 2008
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
+ *   Richard Newman <rnewman@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -122,16 +123,17 @@
 
 #define CREATE_MOZ_FAVICONS NS_LITERAL_CSTRING( \
   "CREATE TABLE moz_favicons (" \
     "  id INTEGER PRIMARY KEY" \
     ", url LONGVARCHAR UNIQUE" \
     ", data BLOB" \
     ", mime_type VARCHAR(32)" \
     ", expiration LONG" \
+    ", guid TEXT" \
   ")" \
 )
 
 #define CREATE_MOZ_BOOKMARKS NS_LITERAL_CSTRING( \
   "CREATE TABLE moz_bookmarks (" \
     "  id INTEGER PRIMARY KEY" \
     ", type INTEGER" \
     ", fk INTEGER DEFAULT NULL" /* place_id */ \